| //! A radioactive stabilization of the [`ptr_meta` RFC][rfc]. |
| //! |
| //! [rfc]: https://rust-lang.github.io/rfcs/2580-ptr-meta.html |
| //! |
| //! # Usage |
| //! |
| //! ## Sized types |
| //! |
| //! All `Sized` types have `Pointee` implemented for them with a blanket implementation. You do not |
| //! need to derive `Pointee` for these types. |
| //! |
| //! ## `slice`s and `str`s |
| //! |
| //! These core types have implementations built in. |
| //! |
| //!## `dyn Any` |
| //! |
| //! The trait object for this standard library type comes with an implementation built in. |
| //! |
| //! ## Structs with a DST as its last field |
| //! |
| //! You can derive `Pointee` for structs with a trailing DST: |
| //! |
| //! ``` |
| //! use ptr_meta::Pointee; |
| //! |
| //! #[derive(Pointee)] |
| //! struct Block<H, T> { |
| //! header: H, |
| //! elements: [T], |
| //! } |
| //! ``` |
| //! |
| //! Note that this will only work when the last field is guaranteed to be a DST. Structs with a |
| //! generic last field may have a conflicting blanket impl since the generic type may be `Sized`. In |
| //! these cases, a collection of specific implementations may be required with the generic parameter |
| //! set to a slice, `str`, or specific trait object. |
| //! |
| //! ## Trait objects |
| //! |
| //! You can generate a `Pointee` implementation for trait objects: |
| //! |
| //! ``` |
| //! use ptr_meta::pointee; |
| //! |
| //! // Generates Pointee for dyn Stringy |
| //! #[pointee] |
| //! trait Stringy { |
| //! fn as_string(&self) -> String; |
| //! } |
| //! ``` |
| |
| #![cfg_attr(not(feature = "std"), no_std)] |
| |
| mod impls; |
| |
| use core::{alloc::Layout, cmp, fmt, hash, marker::PhantomData, ptr}; |
| |
| pub use ptr_meta_derive::{pointee, Pointee}; |
| |
| /// Provides the pointer metadata type of any pointed-to type. |
| /// |
| /// # Pointer metadata |
| /// |
| /// Raw pointer types and reference types in Rust can be thought of as made of two parts: a data |
| /// pointer that contains the memory address of the value, and some additional metadata. |
| /// |
| /// For statically-sized types (that implement `Sized`) as well as `extern` types, pointers are said |
| /// to be "thin". That is, their metadata is zero-sized and its type is `()`. |
| /// |
| /// Pointers to [dynamically-sized types][dst] are said to be "wide" or "fat", and they have |
| /// metadata that is not zero-sized: |
| /// |
| /// * For structs with a DST as the last field, the metadata of the struct is the metadata of the |
| /// last field. |
| /// * For the `str` type, the metadata is the length in bytes as a `usize`. |
| /// * For slice types like `[T]`, the metadata is the length in items as a `usize`. |
| /// * For trait objects like `dyn SomeTrait`, the metadata is [`DynMetadata<Self>`][DynMetadata] |
| /// (e.g. `DynMetadata<dyn SomeTrait>`) which contains a pointer to the trait object's vtable. |
| /// |
| /// In the future, the Rust language may gain new kinds of types that have different pointer |
| /// metadata. |
| /// |
| /// [dst]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#dynamically-sized-types-dsts |
| /// |
| /// |
| /// # The `Pointee` trait |
| /// |
| /// The point of this trait is its associated `Metadata` type, which may be `()`, `usize`, or |
| /// `DynMetadata<_>` as described above. It is automatically implemented for `Sized` types, slices, |
| /// and `str`s. An implementation can be generated for structs with a trailing DST and trait objects |
| /// using the [derive macro] and [attribute macro] respectively. |
| /// |
| /// [derive macro]: ptr_meta_derive::Pointee |
| /// [attribute macro]: ptr_meta_derive::pointee |
| /// |
| /// # Usage |
| /// |
| /// Raw pointers can be decomposed into the data address and metadata components with their |
| /// [`to_raw_parts`] methods. |
| /// |
| /// Alternatively, metadata alone can be extracted with the [`metadata`] function. A reference can |
| /// be passed to [`metadata`] and be implicitly coerced to a pointer. |
| /// |
| /// A (possibly wide) pointer can be put back together from its address and metadata with |
| /// [`from_raw_parts`] or [`from_raw_parts_mut`]. |
| /// |
| /// [`to_raw_parts`]: PtrExt::to_raw_parts |
| pub trait Pointee { |
| /// The type for metadata in pointers and references to `Self`. |
| type Metadata: Copy + Send + Sync + Ord + hash::Hash + Unpin; |
| } |
| |
| impl<T> Pointee for T { |
| type Metadata = (); |
| } |
| |
| impl<T> Pointee for [T] { |
| type Metadata = usize; |
| } |
| |
| impl Pointee for str { |
| type Metadata = usize; |
| } |
| |
| #[cfg(feature = "std")] |
| impl Pointee for ::std::ffi::CStr { |
| type Metadata = usize; |
| } |
| |
| #[cfg(feature = "std")] |
| impl Pointee for ::std::ffi::OsStr { |
| type Metadata = usize; |
| } |
| |
| #[repr(C)] |
| pub(crate) union PtrRepr<T: Pointee + ?Sized> { |
| pub(crate) const_ptr: *const T, |
| pub(crate) mut_ptr: *mut T, |
| pub(crate) components: PtrComponents<T>, |
| } |
| |
| #[repr(C)] |
| pub(crate) struct PtrComponents<T: Pointee + ?Sized> { |
| pub(crate) data_address: *const (), |
| pub(crate) metadata: <T as Pointee>::Metadata, |
| } |
| |
| impl<T: Pointee + ?Sized> Clone for PtrComponents<T> { |
| fn clone(&self) -> Self { |
| Self { |
| data_address: self.data_address.clone(), |
| metadata: self.metadata.clone(), |
| } |
| } |
| } |
| |
| impl<T: Pointee + ?Sized> Copy for PtrComponents<T> {} |
| |
| /// Returns the metadata component of a pointer. |
| /// |
| /// Values of type `*mut T`, `&T`, or `&mut T` can be passed directly to this function as they |
| /// implicitly coerce to `*const T`. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use ptr_meta::metadata; |
| /// |
| /// assert_eq!(metadata("foo"), 3_usize); |
| /// ``` |
| pub fn metadata<T: Pointee + ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata { |
| unsafe { PtrRepr { const_ptr: ptr }.components.metadata } |
| } |
| |
| /// Forms a (possibly wide) raw pointer from a data address and metadata. |
| /// |
| /// This function is safe to call, but the returned pointer is not necessarily safe to dereference. |
| /// For slices, see the documentation of [`slice::from_raw_parts`] for safety requirements. For |
| /// trait objects, the metadata must come from a pointer to the same underlying erased type. |
| /// |
| /// [`slice::from_raw_parts`]: core::slice::from_raw_parts |
| pub fn from_raw_parts<T: Pointee + ?Sized>( |
| data_address: *const (), |
| metadata: <T as Pointee>::Metadata, |
| ) -> *const T { |
| unsafe { |
| PtrRepr { |
| components: PtrComponents { |
| data_address, |
| metadata, |
| }, |
| } |
| .const_ptr |
| } |
| } |
| |
| /// Performs the same functionality as [`from_raw_parts`], except that a raw `*mut` pointer is |
| /// returned, as opposed to a raw `*const` pointer. |
| /// |
| /// See the documentation of [`from_raw_parts`] for more details. |
| pub fn from_raw_parts_mut<T: Pointee + ?Sized>( |
| data_address: *mut (), |
| metadata: <T as Pointee>::Metadata, |
| ) -> *mut T { |
| unsafe { |
| PtrRepr { |
| components: PtrComponents { |
| data_address, |
| metadata, |
| }, |
| } |
| .mut_ptr |
| } |
| } |
| |
| /// Extension methods for [`NonNull`](core::ptr::NonNull). |
| pub trait NonNullExt<T: Pointee + ?Sized> { |
| /// Creates a new non-null pointer from its raw parts. |
| fn from_raw_parts(raw: ptr::NonNull<()>, meta: <T as Pointee>::Metadata) -> Self; |
| /// Converts a non-null pointer to its raw parts. |
| fn to_raw_parts(self) -> (ptr::NonNull<()>, <T as Pointee>::Metadata); |
| } |
| |
| impl<T: Pointee + ?Sized> NonNullExt<T> for ptr::NonNull<T> { |
| fn from_raw_parts(raw: ptr::NonNull<()>, meta: <T as Pointee>::Metadata) -> Self { |
| unsafe { Self::new_unchecked(from_raw_parts_mut(raw.as_ptr(), meta)) } |
| } |
| |
| fn to_raw_parts(self) -> (ptr::NonNull<()>, <T as Pointee>::Metadata) { |
| let (raw, meta) = PtrExt::to_raw_parts(self.as_ptr()); |
| unsafe { (ptr::NonNull::new_unchecked(raw), meta) } |
| } |
| } |
| |
| /// Extension methods for pointers. |
| pub trait PtrExt<T: Pointee + ?Sized> { |
| /// The type's raw pointer (`*const ()` or `*mut ()`). |
| type Raw; |
| |
| /// Decompose a (possibly wide) pointer into its address and metadata components. |
| /// |
| /// The pointer can be later reconstructed with [`from_raw_parts`]. |
| fn to_raw_parts(self) -> (Self::Raw, <T as Pointee>::Metadata); |
| } |
| |
| impl<T: Pointee + ?Sized> PtrExt<T> for *const T { |
| type Raw = *const (); |
| |
| fn to_raw_parts(self) -> (Self::Raw, <T as Pointee>::Metadata) { |
| unsafe { |
| (&self as *const Self) |
| .cast::<(Self::Raw, <T as Pointee>::Metadata)>() |
| .read() |
| } |
| } |
| } |
| |
| impl<T: Pointee + ?Sized> PtrExt<T> for *mut T { |
| type Raw = *mut (); |
| |
| fn to_raw_parts(self) -> (Self::Raw, <T as Pointee>::Metadata) { |
| unsafe { |
| (&self as *const Self) |
| .cast::<(Self::Raw, <T as Pointee>::Metadata)>() |
| .read() |
| } |
| } |
| } |
| |
| /// The metadata for a `Dyn = dyn SomeTrait` trait object type. |
| /// |
| /// It is a pointer to a vtable (virtual call table) that represents all the necessary information |
| /// to manipulate the concrete type stored inside a trait object. The vtable notably contains: |
| /// |
| /// * Type size |
| /// * Type alignment |
| /// * A pointer to the type’s `drop_in_place` impl (may be a no-op for plain-old-data) |
| /// * Pointers to all the methods for the type’s implementation of the trait |
| /// |
| /// Note that the first three are special because they’re necessary to allocate, drop, and |
| /// deallocate any trait object. |
| /// |
| /// It is possible to name this struct with a type parameter that is not a `dyn` trait object (for |
| /// example `DynMetadata<u64>`), but not to obtain a meaningful value of that struct. |
| #[repr(transparent)] |
| pub struct DynMetadata<Dyn: ?Sized> { |
| vtable_ptr: &'static VTable, |
| phantom: PhantomData<Dyn>, |
| } |
| |
| #[repr(C)] |
| struct VTable { |
| drop_in_place: fn(*mut ()), |
| size_of: usize, |
| align_of: usize, |
| } |
| |
| impl<Dyn: ?Sized> DynMetadata<Dyn> { |
| /// Returns the size of the type associated with this vtable. |
| pub fn size_of(self) -> usize { |
| self.vtable_ptr.size_of |
| } |
| |
| /// Returns the alignment of the type associated with this vtable. |
| pub fn align_of(self) -> usize { |
| self.vtable_ptr.align_of |
| } |
| |
| /// Returns the size and alignment together as a `Layout`. |
| pub fn layout(self) -> Layout { |
| unsafe { Layout::from_size_align_unchecked(self.size_of(), self.align_of()) } |
| } |
| } |
| |
| unsafe impl<Dyn: ?Sized> Send for DynMetadata<Dyn> {} |
| unsafe impl<Dyn: ?Sized> Sync for DynMetadata<Dyn> {} |
| impl<Dyn: ?Sized> fmt::Debug for DynMetadata<Dyn> { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| f.debug_tuple("DynMetadata") |
| .field(&(self.vtable_ptr as *const VTable)) |
| .finish() |
| } |
| } |
| impl<Dyn: ?Sized> Unpin for DynMetadata<Dyn> {} |
| impl<Dyn: ?Sized> Copy for DynMetadata<Dyn> {} |
| impl<Dyn: ?Sized> Clone for DynMetadata<Dyn> { |
| #[inline] |
| fn clone(&self) -> Self { |
| *self |
| } |
| } |
| impl<Dyn: ?Sized> cmp::Eq for DynMetadata<Dyn> {} |
| impl<Dyn: ?Sized> cmp::PartialEq for DynMetadata<Dyn> { |
| #[inline] |
| fn eq(&self, other: &Self) -> bool { |
| ptr::eq(self.vtable_ptr, other.vtable_ptr) |
| } |
| } |
| impl<Dyn: ?Sized> cmp::Ord for DynMetadata<Dyn> { |
| #[inline] |
| fn cmp(&self, other: &Self) -> cmp::Ordering { |
| (self.vtable_ptr as *const VTable).cmp(&(other.vtable_ptr as *const VTable)) |
| } |
| } |
| impl<Dyn: ?Sized> cmp::PartialOrd for DynMetadata<Dyn> { |
| #[inline] |
| fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { |
| Some(self.cmp(other)) |
| } |
| } |
| impl<Dyn: ?Sized> hash::Hash for DynMetadata<Dyn> { |
| fn hash<H: hash::Hasher>(&self, hasher: &mut H) { |
| ptr::hash(self.vtable_ptr, hasher) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::{from_raw_parts, pointee, Pointee, PtrExt}; |
| use crate as ptr_meta; |
| |
| fn test_pointee<T: Pointee + ?Sized>(value: &T) { |
| let ptr = value as *const T; |
| let (raw, meta) = PtrExt::to_raw_parts(ptr); |
| let re_ptr = from_raw_parts::<T>(raw, meta); |
| assert_eq!(ptr, re_ptr); |
| } |
| |
| #[test] |
| fn sized_types() { |
| test_pointee(&()); |
| test_pointee(&42); |
| test_pointee(&true); |
| test_pointee(&[1, 2, 3, 4]); |
| |
| struct TestUnit; |
| |
| test_pointee(&TestUnit); |
| |
| #[allow(dead_code)] |
| struct TestStruct { |
| a: (), |
| b: i32, |
| c: bool, |
| } |
| |
| test_pointee(&TestStruct { |
| a: (), |
| b: 42, |
| c: true, |
| }); |
| |
| struct TestTuple((), i32, bool); |
| |
| test_pointee(&TestTuple((), 42, true)); |
| |
| struct TestGeneric<T>(T); |
| |
| test_pointee(&TestGeneric(42)); |
| } |
| |
| #[test] |
| fn unsized_types() { |
| test_pointee("hello world"); |
| test_pointee(&[1, 2, 3, 4] as &[i32]); |
| } |
| |
| #[test] |
| fn trait_objects() { |
| #[pointee] |
| trait TestTrait { |
| fn foo(&self); |
| } |
| |
| struct A; |
| |
| impl TestTrait for A { |
| fn foo(&self) {} |
| } |
| |
| let trait_object = &A as &dyn TestTrait; |
| |
| test_pointee(trait_object); |
| |
| let (_, meta) = PtrExt::to_raw_parts(trait_object as *const dyn TestTrait); |
| |
| assert_eq!(meta.size_of(), 0); |
| assert_eq!(meta.align_of(), 1); |
| |
| struct B(i32); |
| |
| impl TestTrait for B { |
| fn foo(&self) {} |
| } |
| |
| let b = B(42); |
| let trait_object = &b as &dyn TestTrait; |
| |
| test_pointee(trait_object); |
| |
| let (_, meta) = PtrExt::to_raw_parts(trait_object as *const dyn TestTrait); |
| |
| assert_eq!(meta.size_of(), 4); |
| assert_eq!(meta.align_of(), 4); |
| } |
| |
| #[test] |
| fn last_field_dst() { |
| #[allow(dead_code)] |
| #[derive(Pointee)] |
| struct Test<H, T> { |
| head: H, |
| tail: [T], |
| } |
| |
| #[allow(dead_code)] |
| #[derive(Pointee)] |
| struct TestDyn { |
| tail: dyn core::any::Any, |
| } |
| |
| #[pointee] |
| trait TestTrait {} |
| |
| #[allow(dead_code)] |
| #[derive(Pointee)] |
| struct TestCustomDyn { |
| tail: dyn TestTrait, |
| } |
| } |
| } |