Skip to content

Commit d3b40c7

Browse files
committed
implement ErasedPtr as newtype
This changes ErasedPtr from type alias to a newtype implementation. Thus ErasedPtr implements few methods now: * `as_unit_ptr(&self) -> *const ()` getting the pointer value for diagnostics. * `with(FnOnce(&E)-> T)` and `with_mut(FnOnce(&E)-> T)` are moved from the Eraseable trait to ErasedPtr.
1 parent 5277f7c commit d3b40c7

File tree

2 files changed

+86
-63
lines changed

2 files changed

+86
-63
lines changed

crates/erasable/src/lib.rs

+69-57
Original file line numberDiff line numberDiff line change
@@ -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))]
53119
pub(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)]
329341
pub 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
670682
unsafe 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;

crates/erasable/tests/smoke.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn erasing() {
1313
let boxed: Box<Big> = Box::new(Big::default());
1414
let ptr = &*boxed as *const _ as usize;
1515
let erased: ErasedPtr = ErasablePtr::erase(boxed);
16-
assert_eq!(erased.as_ptr() as usize, ptr);
16+
assert_eq!(erased.as_unit_ptr() as usize, ptr);
1717
let boxed: Box<Big> = unsafe { ErasablePtr::unerase(erased) };
1818
assert_eq!(&*boxed as *const _ as usize, ptr);
1919
}
@@ -40,7 +40,17 @@ fn with_fn() {
4040
let erased: ErasedPtr = ErasablePtr::erase(boxed);
4141

4242
unsafe {
43-
<Box<Big> as ErasablePtr>::with(&erased, |bigbox| {
43+
// clippy errs here:
44+
// warning: you seem to be trying to use `&Box<T>`. Consider using just `&T`
45+
// --> crates/erasable/tests/smoke.rs:45:30
46+
// |
47+
// 45 | erased.with(|bigbox: &Box<Big>| {
48+
// | ^^^^^^^^^ help: try: `&Big`
49+
//
50+
// We really need to borrow a &Box<Big> in this case because that what we constructed
51+
// the ErasedPtr from.
52+
#[allow(clippy::borrowed_box)]
53+
erased.with(|bigbox: &Box<Big>| {
4454
assert_eq!(*bigbox, Default::default());
4555
})
4656
}
@@ -56,7 +66,7 @@ fn with_mut_fn() {
5666
let mut erased: ErasedPtr = ErasablePtr::erase(boxed);
5767

5868
unsafe {
59-
<Box<Big> as ErasablePtr>::with_mut(&mut erased, |bigbox| {
69+
erased.with_mut(|bigbox: &mut Box<Big>| {
6070
bigbox.0[0] = 123456;
6171
assert_ne!(*bigbox, Default::default());
6272
})
@@ -71,17 +81,18 @@ fn with_mut_fn_replacethis() {
7181
let boxed: Box<Big> = Default::default();
7282

7383
let mut erased: ErasedPtr = ErasablePtr::erase(boxed);
74-
let e1 = erased.as_ptr();
84+
let e1 = erased.as_unit_ptr();
85+
7586
unsafe {
76-
<Box<Big> as ErasablePtr>::with_mut(&mut erased, |bigbox| {
87+
erased.with_mut(|bigbox: &mut Box<Big>| {
7788
let mut newboxed: Box<Big> = Default::default();
7889
newboxed.0[0] = 123456;
7990
*bigbox = newboxed;
8091
assert_ne!(*bigbox, Default::default());
8192
})
8293
}
8394

84-
let e2 = erased.as_ptr();
95+
let e2 = erased.as_unit_ptr();
8596
assert_ne!(e1, e2);
8697

8798
// drop it, otherwise we would leak memory here

0 commit comments

Comments
 (0)