pub unsafe trait ErasablePtr {
// Required methods
fn erase(this: Self) -> ErasedPtr;
unsafe fn unerase(this: ErasedPtr) -> Self;
}Expand description
A (smart) pointer type that can be type-erased (making a thin pointer).
When implementing this trait, you should implement it for all Erasable pointee types.
Implementing this trait allows use of the pointer in erased contexts, such as Thin.
§Safety
A pointer type which is erasable must not include shared mutability before indirection.
Equivalently, the erased pointer produced by calling erase on some P must be the same
both before and after performing any set of operations on &P. &mut P methods (notably
DerefMut) are still allowed to mutate the pointer value, if necessary.
Additionally, the address of the deref target must be independent of the address of the pointer.
For example, Box implements ErasablePtr because it’s a pointer to a managed heap allocation.
Lazy, however,
Derefs into its own location, and as such, can not implement ErasablePtr.
This is similar to (but distinct from!) the guarantees required by
Pin or
StableDeref.
Pin requires no access to &mut P/&mut P::target, but these remain safe
even when using Thin through Thin::with_mut and DerefMut for Thin.
StableDeref requires the deref target to not change between invocations,
but that is completely fine behavior for ErasablePtr types.
§Examples
use erasable::*;
#[derive(Debug)]
struct MyBox<T: ?Sized>(Box<T>);
unsafe impl<T: ?Sized> ErasablePtr for MyBox<T>
where
T: Erasable
{
fn erase(this: Self) -> ErasedPtr {
ErasablePtr::erase(this.0)
}
unsafe fn unerase(this: ErasedPtr) -> Self {
Self(ErasablePtr::unerase(this))
}
}
let array = [0; 10];
let boxed = MyBox(Box::new(array));
let thin_box: Thin<MyBox<_>> = boxed.into();
dbg!(thin_box);§Counterexamples
This implementation of ErasablePtr is unsound
because it features shared mutability before indirection:
struct Pls {
inner: Cell<Box<u8>>,
}
unsafe impl ErasablePtr for Pls {
fn erase(this: Self) -> ErasedPtr { ErasablePtr::erase(this.inner.into_inner()) }
unsafe fn unerase(this: ErasedPtr) -> Self {
Pls { inner: Cell::new(ErasablePtr::unerase(this)) }
}
}
impl Pls {
fn mutate(&self, to: Box<u8>) { self.inner.set(to); }
}
let thin = Thin::from(Pls { inner: Cell::new(Box::new(0)) });
Thin::with(&thin, |pls| pls.mutate(Box::new(1))); // drops box(0), leaks box(1)
drop(thin); // `thin` is still Pls(Box(0)); use-after-freeThis implementation of ErasablePtr is unsound
because it dereferences to the interior of the type:
struct Why {
inner: Box<u8>,
}
unsafe impl ErasablePtr for Why {
fn erase(this: Self) -> ErasedPtr { ErasablePtr::erase(this.inner) }
unsafe fn unerase(this: ErasedPtr) -> Self { Why { inner: ErasablePtr::unerase(this) } }
}
impl Deref for Why {
type Target = Box<u8>;
fn deref(&self) -> &Box<u8> { &self.inner }
}
let thin = Thin::from(Why { inner: Box::new(0) });
let _: &Box<u8> = thin.deref(); // use-after-free; cannot deref to value that does not existRequired Methods§
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.