Reputation: 63
I'm calling a C constructor function keyNew
that allocates memory for a Key
struct and returns a *mut Key
to the Rust side. Is it appropriate to use Box::from_raw
to wrap the pointer and take ownership of it?
I could also return the raw pointer directly, but this makes for a very ugly API and is not idiomatic to Rust.
I also want to implement the Drop Trait on the Key, such that the destructor function keyDel
is automatically called, which would be an improvement over the manual calls in C. This is a much nicer API in my opinion. However implementing the Drop Trait requires that the Copy Trait is not implemented, so dereferencing the raw pointer is no longer possible due to "move out of dereferenced content".
They Key was generated by rust-bindgen
#[repr(C)]
#[derive(Debug, Clone)]
pub struct Key {
_unused: [u8; 0],
}
The constructor for Key
is implemented like this
fn new() -> Box<Key> {
unsafe { Box::from_raw(keyNew(0 as *const i8)) }
}
And the destructor
impl Drop for Key {
fn drop(&mut self) {
unsafe { keyDel(self) };
}
}
It all works wonderfully, I can use a Box<Key
like a Key
due to Deref and keyDel
is automatically called when the Box<Key
goes out of scpe. However I am not sure if it's appropriate, since the Rust documentation has this to say about Box::from_raw
After calling this function, the raw pointer is owned by the resulting Box. Specifically, the Box destructor will call the destructor of T and free the allocated memory. Since the way Box allocates and releases memory is unspecified, the only valid pointer to pass to this function is the one taken from another Box via the Box::into_raw function.
I am not calling Box::into_raw
later, so is this all still valid memory-wise? And if not, what is an alternative way to take ownership of the returned *mut Key
?
Upvotes: 6
Views: 3490
Reputation: 161467
Is it appropriate to use Box::from_raw to wrap the pointer and take ownership of it?
The documentation for from_raw
answers this for you:
Since the way Box allocates and releases memory is unspecified, the only valid pointer to pass to this function is the one taken from another Box via the
Box::into_raw
function.
which means that your current usage is unspecified.
I also want to implement the Drop Trait on the Key, such that the destructor function keyDel is automatically called
You should not implement Drop
for Key
because Key
is not allocated by Rust. You would ideally make your own wrapper type that uses its own drop to call keyDel
with the pointer. For instance:
struct KeyWrapper {
ptr: NonNull<Key>
}
impl Drop for KeyWrapper {
fn drop(&mut self) {
keyDel(self.ptr.as_ptr())
}
}
impl KeyWrapper {
fn new() {
KeyWrapper {
ptr: NonNull::new(keyNew(0 as *const i8))
}
}
fn someUtil(&self) {
// As an example, you could call through to some C function.
keySomeUtil(self.ptr.as_ptr())
}
}
This way on the Rust side you are only interacting with the type that wraps the pointer, and it will call keyDel
when it is dropped. It is your job to make sure this wrapper type only performs safe operations via the C API so as not to invalidate Rust's safety guarantees.
Related:
Upvotes: 3