[][src]Trait erasable::ErasablePtr

pub unsafe trait ErasablePtr {
    fn erase(this: Self) -> ErasedPtr;
unsafe fn unerase(this: ErasedPtr) -> Self; }

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-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

fn erase(this: Self) -> ErasedPtr

Turn this erasable pointer into an erased pointer.

To retrieve the original pointer, use unerase.

unsafe fn unerase(this: ErasedPtr) -> Self

Unerase this erased pointer.

Safety

The erased pointer must have been created by erase.

Loading content...

Implementations on Foreign Types

impl<T: Sized> ErasablePtr for NonNull<T> where
    T: Erasable
[src]

impl<P> ErasablePtr for Pin<P> where
    P: ErasablePtr + Deref
[src]

impl<T: ?Sized> ErasablePtr for Box<T> where
    T: Erasable
[src]

impl<T: ?Sized> ErasablePtr for Arc<T> where
    T: Erasable
[src]

impl<T: ?Sized> ErasablePtr for Rc<T> where
    T: Erasable
[src]

Loading content...

Implementors

impl<'_, T: ?Sized> ErasablePtr for &'_ T where
    T: Erasable
[src]

impl<'_, T: ?Sized> ErasablePtr for &'_ mut T where
    T: Erasable
[src]

impl<P: ErasablePtr> ErasablePtr for Thin<P>[src]

Loading content...