Add safe constructors for untyped pointers Ptr and PtrMut (#6539)
# Objective
Currently, `Ptr` and `PtrMut` can only be constructed via unsafe code. This means that downgrading a reference to an untyped pointer is very cumbersome, despite being a very simple operation.
## Solution
Define conversions for easily and safely constructing untyped pointers. This is the non-owned counterpart to `OwningPtr::make`.
Before:
```rust
let ptr = unsafe { PtrMut::new(NonNull::from(&mut value).cast()) };
```
After:
```rust
let ptr = PtrMut::from(&mut value);
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
635320f172
commit
f2f8f9097f
@ -3,7 +3,7 @@
|
|||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
use core::{
|
use core::{
|
||||||
cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit, num::NonZeroUsize, ptr::NonNull,
|
cell::UnsafeCell, marker::PhantomData, mem::ManuallyDrop, num::NonZeroUsize, ptr::NonNull,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Type-erased borrow of some unknown type chosen when constructing this type.
|
/// Type-erased borrow of some unknown type chosen when constructing this type.
|
||||||
@ -98,6 +98,9 @@ macro_rules! impl_ptr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl_ptr!(Ptr);
|
impl_ptr!(Ptr);
|
||||||
|
impl_ptr!(PtrMut);
|
||||||
|
impl_ptr!(OwningPtr);
|
||||||
|
|
||||||
impl<'a> Ptr<'a> {
|
impl<'a> Ptr<'a> {
|
||||||
/// Transforms this [`Ptr`] into an [`PtrMut`]
|
/// Transforms this [`Ptr`] into an [`PtrMut`]
|
||||||
///
|
///
|
||||||
@ -127,7 +130,16 @@ impl<'a> Ptr<'a> {
|
|||||||
self.0.as_ptr()
|
self.0.as_ptr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl_ptr!(PtrMut);
|
|
||||||
|
impl<'a, T> From<&'a T> for Ptr<'a> {
|
||||||
|
#[inline]
|
||||||
|
fn from(val: &'a T) -> Self {
|
||||||
|
// SAFETY: The returned pointer has the same lifetime as the passed reference.
|
||||||
|
// Access is immutable.
|
||||||
|
unsafe { Self::new(NonNull::from(val).cast()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> PtrMut<'a> {
|
impl<'a> PtrMut<'a> {
|
||||||
/// Transforms this [`PtrMut`] into an [`OwningPtr`]
|
/// Transforms this [`PtrMut`] into an [`OwningPtr`]
|
||||||
///
|
///
|
||||||
@ -157,15 +169,24 @@ impl<'a> PtrMut<'a> {
|
|||||||
self.0.as_ptr()
|
self.0.as_ptr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl_ptr!(OwningPtr);
|
|
||||||
|
impl<'a, T> From<&'a mut T> for PtrMut<'a> {
|
||||||
|
#[inline]
|
||||||
|
fn from(val: &'a mut T) -> Self {
|
||||||
|
// SAFETY: The returned pointer has the same lifetime as the passed reference.
|
||||||
|
// The reference is mutable, and thus will not alias.
|
||||||
|
unsafe { Self::new(NonNull::from(val).cast()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> OwningPtr<'a> {
|
impl<'a> OwningPtr<'a> {
|
||||||
/// Consumes a value and creates an [`OwningPtr`] to it while ensuring a double drop does not happen.
|
/// Consumes a value and creates an [`OwningPtr`] to it while ensuring a double drop does not happen.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn make<T, F: FnOnce(OwningPtr<'_>) -> R, R>(val: T, f: F) -> R {
|
pub fn make<T, F: FnOnce(OwningPtr<'_>) -> R, R>(val: T, f: F) -> R {
|
||||||
let mut temp = MaybeUninit::new(val);
|
let mut temp = ManuallyDrop::new(val);
|
||||||
// SAFETY: `temp.as_mut_ptr()` is a reference to a local value on the stack, so it cannot be null
|
// SAFETY: The value behind the pointer will not get dropped or observed later,
|
||||||
let ptr = unsafe { NonNull::new_unchecked(temp.as_mut_ptr().cast::<u8>()) };
|
// so it's safe to promote it to an owning pointer.
|
||||||
f(Self(ptr, PhantomData))
|
f(unsafe { PtrMut::from(&mut *temp).promote() })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes the [`OwningPtr`] to obtain ownership of the underlying data of type `T`.
|
/// Consumes the [`OwningPtr`] to obtain ownership of the underlying data of type `T`.
|
||||||
|
|||||||
@ -475,7 +475,7 @@ impl<T: for<'a> Deserialize<'a> + Reflect> FromType<T> for ReflectDeserialize {
|
|||||||
/// type_registry.register::<Reflected>();
|
/// type_registry.register::<Reflected>();
|
||||||
///
|
///
|
||||||
/// let mut value = Reflected("Hello world!".to_string());
|
/// let mut value = Reflected("Hello world!".to_string());
|
||||||
/// let value = unsafe { Ptr::new(NonNull::from(&mut value).cast()) };
|
/// let value = Ptr::from(&value);
|
||||||
///
|
///
|
||||||
/// let reflect_data = type_registry.get(std::any::TypeId::of::<Reflected>()).unwrap();
|
/// let reflect_data = type_registry.get(std::any::TypeId::of::<Reflected>()).unwrap();
|
||||||
/// let reflect_from_ptr = reflect_data.data::<ReflectFromPtr>().unwrap();
|
/// let reflect_from_ptr = reflect_data.data::<ReflectFromPtr>().unwrap();
|
||||||
@ -534,8 +534,6 @@ impl<T: Reflect> FromType<T> for ReflectFromPtr {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::ptr::NonNull;
|
|
||||||
|
|
||||||
use crate::{GetTypeRegistration, ReflectFromPtr, TypeRegistration};
|
use crate::{GetTypeRegistration, ReflectFromPtr, TypeRegistration};
|
||||||
use bevy_ptr::{Ptr, PtrMut};
|
use bevy_ptr::{Ptr, PtrMut};
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
@ -560,8 +558,7 @@ mod test {
|
|||||||
|
|
||||||
let mut value = Foo { a: 1.0 };
|
let mut value = Foo { a: 1.0 };
|
||||||
{
|
{
|
||||||
// SAFETY: lifetime doesn't outlive original value, access is unique
|
let value = PtrMut::from(&mut value);
|
||||||
let value = unsafe { PtrMut::new(NonNull::from(&mut value).cast()) };
|
|
||||||
// SAFETY: reflect_from_ptr was constructed for the correct type
|
// SAFETY: reflect_from_ptr was constructed for the correct type
|
||||||
let dyn_reflect = unsafe { reflect_from_ptr.as_reflect_ptr_mut(value) };
|
let dyn_reflect = unsafe { reflect_from_ptr.as_reflect_ptr_mut(value) };
|
||||||
match dyn_reflect.reflect_mut() {
|
match dyn_reflect.reflect_mut() {
|
||||||
@ -573,10 +570,8 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// SAFETY: lifetime doesn't outlive original value
|
|
||||||
let value = unsafe { Ptr::new(NonNull::from(&mut value).cast()) };
|
|
||||||
// SAFETY: reflect_from_ptr was constructed for the correct type
|
// SAFETY: reflect_from_ptr was constructed for the correct type
|
||||||
let dyn_reflect = unsafe { reflect_from_ptr.as_reflect_ptr(value) };
|
let dyn_reflect = unsafe { reflect_from_ptr.as_reflect_ptr(Ptr::from(&value)) };
|
||||||
match dyn_reflect.reflect_ref() {
|
match dyn_reflect.reflect_ref() {
|
||||||
bevy_reflect::ReflectRef::Struct(strukt) => {
|
bevy_reflect::ReflectRef::Struct(strukt) => {
|
||||||
let a = strukt.field("a").unwrap().downcast_ref::<f32>().unwrap();
|
let a = strukt.field("a").unwrap().downcast_ref::<f32>().unwrap();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user