Let's say I want to write code like this:
struct Inspector<'a>(&'a u8);
struct Foo<'a> {
value: Box<u8>,
inspector: Option<Inspector<'a>>,
}
fn main() {
let mut foo = Foo { value: Box::new(0), inspector: None };
foo.inspector = Some(Inspector(&foo.value));
}
Currently, the Rust compiler let me do this as long as I don't add a Drop
implementation for Inspector
.
If I add one, the following compile-time error occurs:
foo.value
dropped while still borrowed.Borrow might be used when
foo
is dropped and runs the destructor for typeFoo<'_>
And it is obviously right. In fact, I have taken this example from the nomicon.
Now, here is my problem. Let's say I have a weird implementation of a Box
that do not have any T
in its type parameters.
/// An heap-allocated `T` without generic parameters.
struct MyBox {
data: NonNull<u8>,
/// SAFETY:
/// Caller must ensure the value will not be
/// used again.
drop_fn: unsafe fn(*mut u8),
layout: Layout,
}
impl MyBox {
fn new<T>(val: T) -> Self {
if mem::size_of::<T>() == 0 {
panic!("T is a ZST");
}
let layout = Layout::new::<T>();
let data = NonNull::new(unsafe { alloc(layout) })
.unwrap_or_else(|| handle_alloc_error(layout));
// pointer refers to uninit owned memory
unsafe { data.cast::<T>().as_ptr().write(val) };
Self {
data,
// SAFETY: See `drop_fn` field for safety guarantees
drop_fn: |data| unsafe { drop_in_place(data as *mut T) },
layout,
}
}
/// Caller must ensure that this box owns a `T`.
unsafe fn trust_mut<T>(&mut self) -> &mut T {
&mut *self.data.cast().as_ptr()
}
}
impl Drop for MyBox {
fn drop(&mut self) {
// SAFETY: Value will not be used again
unsafe { (self.drop_fn)(self.data.as_ptr()) }
unsafe { dealloc(self.data.as_ptr(), self.layout) };
}
}
But this time, Rust's drop checker does not know that MyBox
will drop a T
when its destructor is called. This enables me to write this unsound code:
pub struct Inspector<'a>(&'a u8);
impl Drop for Inspector<'_> {
fn drop(&mut self) {
/* Could try to inspect `self.0` here which might have been dropped */
}
}
pub struct Foo<'a> {
value: Box<u8>,
inspector: Option<Inspector<'a>>,
}
fn main() {
let mut b = MyBox::new(Foo {
value: Box::new(0),
inspector: None,
});
let foo: &mut Foo = unsafe { b.trust_mut() };
foo.inspector = Some(Inspector(&foo.value)); // No error occurs here
}
From this, my question is simple: is there a way to tell the drop checker that it is not ok to have a lifetime bounded to the object when it gets dropped? Because what I basically need is something like PhantomData<T>
without having a T
.