Skip to content

Commit c397f52

Browse files
committed
Add 'with()' and 'with_mut()' as associated functions to ErasedPtr
This allows one to use ErasedPtr where rust won't allow a typed reference. For example in recursive types.
1 parent f44329e commit c397f52

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

crates/erasable/src/lib.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,54 @@ pub unsafe trait ErasablePtr {
185185
///
186186
/// The erased pointer must have been created by `erase`.
187187
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+
/// ```
205+
///
206+
/// The main purpose of this function is to be able implement recursive types that would
207+
/// be otherwise not representable in rust.
208+
///
209+
/// # Safety
210+
///
211+
/// * The erased pointer must have been created by `erase`.
212+
/// * The specified impl type must be the original type.
213+
unsafe fn with<F, T>(this: &ErasedPtr, f: F) -> T
214+
where
215+
Self: Sized,
216+
F: FnOnce(&Self) -> T,
217+
{
218+
f(&ManuallyDrop::new(&Self::unerase(*this)))
219+
}
220+
221+
/// Run a closure on a mutable borrow of the real pointer. Unlike the `Thin<T>` wrapper
222+
/// this does not carry the original type around. Thus it is required to specify the
223+
/// original impl type when calling this function.
224+
///
225+
/// # Safety
226+
///
227+
/// * The erased pointer must have been created by `erase`.
228+
/// * The specified impl type must be the original type.
229+
unsafe fn with_mut<F, T>(this: &mut ErasedPtr, f: F) -> T
230+
where
231+
Self: Sized,
232+
F: FnOnce(&mut Self) -> T,
233+
{
234+
f(&mut ManuallyDrop::new(&mut Self::unerase(*this)))
235+
}
188236
}
189237

190238
/// A pointee type that supports type-erased pointers (thin pointers).

crates/erasable/tests/smoke.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,30 @@ fn thinning() {
3232
Thin::with_mut(&mut thin, |thin| *thin = Default::default());
3333
let boxed = Thin::into_inner(thin);
3434
}
35+
36+
#[test]
37+
fn withfn() {
38+
let boxed: Box<Big> = Default::default();
39+
40+
let erased: ErasedPtr = ErasablePtr::erase(boxed);
41+
42+
unsafe {
43+
<Box<Big> as ErasablePtr>::with(&erased, |bigbox| {
44+
assert_eq!(*bigbox, Default::default());
45+
})
46+
}
47+
}
48+
49+
#[test]
50+
fn with_mut_fn() {
51+
let boxed: Box<Big> = Default::default();
52+
53+
let mut erased: ErasedPtr = ErasablePtr::erase(boxed);
54+
55+
unsafe {
56+
<Box<Big> as ErasablePtr>::with_mut(&mut erased, |bigbox| {
57+
bigbox.0[0] = 123456;
58+
assert_ne!(*bigbox, Default::default());
59+
})
60+
}
61+
}

0 commit comments

Comments
 (0)