//! 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 { //! 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`][DynMetadata] /// (e.g. `DynMetadata`) 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 Pointee for T { type Metadata = (); } impl 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 { pub(crate) const_ptr: *const T, pub(crate) mut_ptr: *mut T, pub(crate) components: PtrComponents, } #[repr(C)] pub(crate) struct PtrComponents { pub(crate) data_address: *const (), pub(crate) metadata: ::Metadata, } impl Clone for PtrComponents { fn clone(&self) -> Self { Self { data_address: self.data_address.clone(), metadata: self.metadata.clone(), } } } impl Copy for PtrComponents {} /// 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(ptr: *const T) -> ::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( data_address: *const (), metadata: ::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( data_address: *mut (), metadata: ::Metadata, ) -> *mut T { unsafe { PtrRepr { components: PtrComponents { data_address, metadata, }, } .mut_ptr } } /// Extension methods for [`NonNull`](core::ptr::NonNull). pub trait NonNullExt { /// Creates a new non-null pointer from its raw parts. fn from_raw_parts(raw: ptr::NonNull<()>, meta: ::Metadata) -> Self; /// Converts a non-null pointer to its raw parts. fn to_raw_parts(self) -> (ptr::NonNull<()>, ::Metadata); } impl NonNullExt for ptr::NonNull { fn from_raw_parts(raw: ptr::NonNull<()>, meta: ::Metadata) -> Self { unsafe { Self::new_unchecked(from_raw_parts_mut(raw.as_ptr(), meta)) } } fn to_raw_parts(self) -> (ptr::NonNull<()>, ::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 { /// 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, ::Metadata); } impl PtrExt for *const T { type Raw = *const (); fn to_raw_parts(self) -> (Self::Raw, ::Metadata) { unsafe { (&self as *const Self) .cast::<(Self::Raw, ::Metadata)>() .read() } } } impl PtrExt for *mut T { type Raw = *mut (); fn to_raw_parts(self) -> (Self::Raw, ::Metadata) { unsafe { (&self as *const Self) .cast::<(Self::Raw, ::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`), but not to obtain a meaningful value of that struct. #[repr(transparent)] pub struct DynMetadata { vtable_ptr: &'static VTable, phantom: PhantomData, } #[repr(C)] struct VTable { drop_in_place: fn(*mut ()), size_of: usize, align_of: usize, } impl DynMetadata { /// 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 Send for DynMetadata {} unsafe impl Sync for DynMetadata {} impl fmt::Debug for DynMetadata { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("DynMetadata") .field(&(self.vtable_ptr as *const VTable)) .finish() } } impl Unpin for DynMetadata {} impl Copy for DynMetadata {} impl Clone for DynMetadata { #[inline] fn clone(&self) -> Self { *self } } impl cmp::Eq for DynMetadata {} impl cmp::PartialEq for DynMetadata { #[inline] fn eq(&self, other: &Self) -> bool { ptr::eq(self.vtable_ptr, other.vtable_ptr) } } impl cmp::Ord for DynMetadata { #[inline] fn cmp(&self, other: &Self) -> cmp::Ordering { (self.vtable_ptr as *const VTable).cmp(&(other.vtable_ptr as *const VTable)) } } impl cmp::PartialOrd for DynMetadata { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl hash::Hash for DynMetadata { fn hash(&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(value: &T) { let ptr = value as *const T; let (raw, meta) = PtrExt::to_raw_parts(ptr); let re_ptr = from_raw_parts::(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); 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 { 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, } } }