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,
Deref
s 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-free
This 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 exist
Required 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.