I want to store references that don’t outlive its container (internally, those references are stored as pointers). Those references are not necessarily set when the container is constructed.
Take this code:
#[repr(C)]
struct Array {
size: usize,
arr: *mut Array,
}
// Drop for Array deallocates array recursively
// must have same representation as Array
#[repr(C)]
struct ShallowArray<'a> {
inner: std::mem::ManuallyDrop<Array>,
_phantom: std::marker::PhantomData<&'a Array>,
}
impl<'a> ShallowArray<'a> {
fn new(size: usize) -> Self {
let layout = Layout::array::<Array>(size).unwrap();
let arr = unsafe { alloc::alloc(layout) } as *mut Array;
unsafe {
std::ptr::write_bytes(arr, 0, layout.size());
}
Self {
inner: std::mem::ManuallyDrop::new(Array { size, arr }),
_phantom: std::marker::PhantomData,
}
}
fn set_i(&'a mut self, idx: usize, obj: &'a Array) {
if idx >= self.inner.size {
panic!("Index out of bounds");
}
unsafe {
*self.inner.arr.add(idx) = Array {
size: obj.size,
arr: obj.arr,
};
}
}
}
impl Drop for ShallowArray<'_> {
fn drop(&mut self) {
// deallocate only the first level
let layout = Layout::array::<Array>(self.inner.size).unwrap();
unsafe { alloc::dealloc(self.inner.arr as *mut u8, layout) };
}
}
fn test() {
let arr1 = Array {
size: 0,
arr: std::ptr::null_mut(),
};
let mut arr = ShallowArray::new(3);
// reverse order of previous declarations is problematic for
// another reason
arr.set_i(0, &arr1);
}
this fails with:
error[E0597]: `arr` does not live long enough
--> src/main.rs:58:5
|
57 | let mut arr = ShallowArray::new(3);
| ------- binding `arr` declared here
58 | arr.set_i(0, &arr1);
| ^^^ borrowed value does not live long enough
59 | }
| -
| |
| `arr` dropped here while still borrowed
| borrow might be used here, when `arr` is dropped and runs the `Drop` code for type `shallow::ShallowArray`
4
Solution
as @kmdreko commented
just change &'a mut self
to &mut self
in set_i
definition
Explanation
it happens since implementing Drop
make the compiler call Drop::drop
when the value goes out of scope
and
&'a self
: means to borrow for a specified lifetime.&self
: means to borrow for just the scope of the function.
Consider following example,
struct MyStruct<'a>{
my_ref : &'a i32,
}
impl<'a> MyStruct<'a> {
pub fn my_method(&'a mut self, my_arg: &'a i32) {
todo!()
}
}
impl<'a> Drop for MyStruct<'a> {
fn drop(&mut self) {
todo!()
}
}
fn main () {
let my_i32 = 0;
let mut my_instance = MyStruct {my_ref: &my_i32};
// ^^^^^^^^^^^^
// 'a start here, 'a is the life time of my_instance
// from declare to out of scope (or dropped)
my_instance.my_method(&my_i32);
// and then we borrow my_instance for 'a as specified in function definition
// that means the borrow is not done until my_instance is dropped
// but since we implemented Drop for MyStruct
// that means we call drop() when out of scope implicitly
drop(my_instance)
// and drop() take ownership
// but you cannot take ownership, since it is still borrowed by my_method()
// we need to wait until end of life of my_instance
// that mean we need to call drop()
// but we cannot call drop . . . so we entered infinte loop
}
2