@@ -47,7 +47,73 @@ use core::{
4747/// The current implementation uses a `struct Erased` with size 0 and align 1.
4848/// If you want to offset the pointer, make sure to cast to a `u8` or other known type pointer first.
4949/// When `Erased` becomes an extern type, it will properly have unknown size and align.
50- pub type ErasedPtr = ptr:: NonNull < Erased > ;
50+ #[ derive( Debug , Copy , Clone , PartialEq ) ]
51+ #[ repr( transparent) ]
52+ pub struct ErasedPtr ( ptr:: NonNull < Erased > ) ;
53+
54+ impl ErasedPtr {
55+ /// Gets the raw pointer as '*const ()' unit type. This keeps the internal representation
56+ /// hidden and is not very useful but for diagnostics like logging memory addresses and
57+ /// comparing pointers for partial equality.
58+ pub fn as_unit_ptr ( & self ) -> * const ( ) {
59+ self . 0 . as_ptr ( ) as * const _
60+ }
61+
62+ /// Run a closure on a borrow of the real pointer. Unlike the `Thin<T>` wrapper this does
63+ /// not carry the original type around. Thus it is required to specify the original impl
64+ /// type as closure parameter.
65+ ///
66+ /// ```
67+ /// # use {erasable::*, std::rc::Rc};
68+ /// let rc: Rc<i32> = Rc::new(123);
69+ ///
70+ /// let erased: ErasedPtr = ErasablePtr::erase(rc);
71+ ///
72+ /// let cloned = unsafe {
73+ /// // must specify a reference to the original type here
74+ /// erased.with(|rc: &Rc<i32>| rc.clone())
75+ /// };
76+ ///
77+ /// assert_eq!(*cloned, 123);
78+ /// # unsafe {<Rc<i32> as ErasablePtr>::unerase(erased)}; // drop it
79+ /// ```
80+ ///
81+ /// # Safety
82+ ///
83+ /// * The erased pointer must have been created by `erase`.
84+ /// * The specified impl type must be the original type.
85+ pub unsafe fn with < E , F , T > ( & self , f : F ) -> T
86+ where
87+ E : ErasablePtr ,
88+ F : FnOnce ( & E ) -> T ,
89+ {
90+ f ( & ManuallyDrop :: new ( <E as ErasablePtr >:: unerase ( * self ) ) )
91+ }
92+
93+ /// Run a closure on a mutable borrow of the real pointer. Unlike the `Thin<T>` wrapper
94+ /// this does not carry the original type around. Thus it is required to specify the
95+ /// original impl type as closure parameter.
96+ ///
97+ /// # Safety
98+ ///
99+ /// * The erased pointer must have been created by `erase`.
100+ /// * The specified impl type must be the original type.
101+ pub unsafe fn with_mut < E , F , T > ( & mut self , f : F ) -> T
102+ where
103+ E : ErasablePtr ,
104+ F : FnOnce ( & mut E ) -> T ,
105+ {
106+ // SAFETY: guard is required to write potentially changed pointer value, even on unwind
107+ let mut this = scopeguard:: guard (
108+ ManuallyDrop :: new ( <E as ErasablePtr >:: unerase ( * self ) ) ,
109+ |unerased| {
110+ ptr:: write ( self , ErasablePtr :: erase ( ManuallyDrop :: into_inner ( unerased) ) ) ;
111+ } ,
112+ ) ;
113+
114+ f ( & mut this)
115+ }
116+ }
51117
52118#[ cfg( not( has_extern_type) ) ]
53119pub ( crate ) use priv_in_pub:: Erased ;
@@ -185,60 +251,6 @@ pub unsafe trait ErasablePtr {
185251 ///
186252 /// The erased pointer must have been created by `erase`.
187253 unsafe fn unerase ( this : ErasedPtr ) -> Self ;
188-
189- /// Run a closure on a borrow of the real pointer. Unlike the `Thin<T>` wrapper this does
190- /// not carry the original type around. Thus it is required to specify the original impl
191- /// type when calling this function.
192- ///
193- /// ```
194- /// # use {erasable::*, std::rc::Rc};
195- /// let rc: Rc<i32> = Rc::new(123);
196- ///
197- /// let erased: ErasedPtr = ErasablePtr::erase(rc);
198- ///
199- /// let cloned = unsafe {
200- /// <Rc<i32> as ErasablePtr>::with(&erased, |rc| rc.clone())
201- /// };
202- ///
203- /// assert_eq!(*cloned, 123);
204- /// # unsafe {<Rc<i32> as ErasablePtr>::unerase(erased)}; // drop it
205- /// ```
206- ///
207- /// The main purpose of this function is to be able implement recursive types that would
208- /// be otherwise not representable in rust.
209- ///
210- /// # Safety
211- ///
212- /// * The erased pointer must have been created by `erase`.
213- /// * The specified impl type must be the original type.
214- unsafe fn with < F , T > ( this : & ErasedPtr , f : F ) -> T
215- where
216- Self : Sized ,
217- F : FnOnce ( & Self ) -> T ,
218- {
219- f ( & ManuallyDrop :: new ( Self :: unerase ( * this) ) )
220- }
221-
222- /// Run a closure on a mutable borrow of the real pointer. Unlike the `Thin<T>` wrapper
223- /// this does not carry the original type around. Thus it is required to specify the
224- /// original impl type when calling this function.
225- ///
226- /// # Safety
227- ///
228- /// * The erased pointer must have been created by `erase`.
229- /// * The specified impl type must be the original type.
230- unsafe fn with_mut < F , T > ( this : & mut ErasedPtr , f : F ) -> T
231- where
232- Self : Sized ,
233- F : FnOnce ( & mut Self ) -> T ,
234- {
235- // SAFETY: guard is required to write potentially changed pointer value, even on unwind
236- let mut that = scopeguard:: guard ( ManuallyDrop :: new ( Self :: unerase ( * this) ) , |unerased| {
237- ptr:: write ( this, ErasablePtr :: erase ( ManuallyDrop :: into_inner ( unerased) ) ) ;
238- } ) ;
239-
240- f ( & mut that)
241- }
242254}
243255
244256/// A pointee type that supports type-erased pointers (thin pointers).
@@ -327,7 +339,7 @@ pub unsafe trait Erasable {
327339/// Erase a pointer.
328340#[ inline( always) ]
329341pub fn erase < T : ?Sized > ( ptr : ptr:: NonNull < T > ) -> ErasedPtr {
330- unsafe { ptr:: NonNull :: new_unchecked ( ptr. as_ptr ( ) as * mut Erased ) }
342+ unsafe { ErasedPtr ( ptr:: NonNull :: new_unchecked ( ptr. as_ptr ( ) as * mut Erased ) ) }
331343}
332344
333345/// Wrapper struct to create thin pointer types.
@@ -670,7 +682,7 @@ where
670682unsafe impl < T : Sized > Erasable for T {
671683 unsafe fn unerase ( this : ErasedPtr ) -> ptr:: NonNull < T > {
672684 // SAFETY: must not read the pointer for the safety of the impl directly below.
673- this. cast ( )
685+ this. 0 . cast ( )
674686 }
675687
676688 const ACK_1_1_0 : bool = true ;
0 commit comments