The main problem here is that dst_buffer
lets you access the heap allocation while a mutable reference to it exists. Mutable references require exclusive access, so you are not allowed to access the buffer directly until you have destroyed the Cursor
.
-pub fn dst_buffer(&mut self) -> &[f32] {
- &self.dst_buffer[..]
-}
Additionally, you should not have a lifetime on the struct. Lifetimes are for references to things outside the struct.
Another issue here regarding the aliasing rules for boxes. The short story is that a Box
asserts exclusive ownership of its heap allocation whenever it is used, including when it is moved, which includes when you move the struct containing it. The long story can be found here. Usually the way to fix it is to replace the Box
with a raw pointer (which you usually wrap in another struct that has the right destructor).
struct AliasedBox<T: ?Sized> {
ptr: *mut T,
}
impl<T: ?Sized> AliasedBox<T> {
fn new(b: Box<T>) -> Self<T> {
let ptr = Box::into_raw(b);
Self { ptr }
}
/// Safety: Once a reference is created using this method, it is
/// invalid to access the contents of the box in any way (including
/// dropping the box) until the last use of the returned reference.
///
/// Moving the aliased box does not count as a use for the purposes
/// of the safety of this method.
unsafe fn get_mut_ref(&mut self) -> &'static mut T {
&mut *self.ptr
}
/// Calling this method counts as a use of the box for the purposes
/// of `get_mut_ref`.
fn into_box(self) -> Box<T> {
let b = unsafe { Box::from_raw(self.ptr) };
std::mem::forget(self);
b
}
}
impl<T: ?Sized> Drop for AliasedBox<T> {
fn drop(&mut self) {
unsafe {
drop(Box::from_raw(self.ptr));
}
}
}
Next, fields are dropped in the order they are declared in the struct, so you are dropping the Box
before you are destroying the Cursor
, which has a mutable reference to the Box
. This is not valid, and you should swap the order of the fields.
The next issue is that making the Cursor
accessible from outside the Context
, you make it possible for the user to grab the mutable reference to the aliased box and hold on to it until after the context is dropped. They can do this by calling Cursor::get_mut
and using std::mem::swap
to swap out the mutable reference inside the Cursor
for some other static mutable reference, e.g. a mutable reference to a global variable or leaked box. To avoid this issue, you should define methods on the Context
that use the cursor in only safe ways, and have the user access the cursor only through such methods.
// or perhaps implement the `Write` trait for `Context`
impl Context {
pub fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
std.dst_cursor.write(data)
}
}
Finally, I assume you want some way to access the box after using the cursor. You can define a method like this:
impl Context {
pub fn into_buffer(self) -> Box<[f32]> {
drop(self.dst_cursor);
self.dst_buffer.into_box()
}
}
Note that the safety of this relies on the fact that the cursor is destroyed before the AliasedBox
is accessed again.