pgackst
pgackst

Reputation: 63

How to take ownership of a C pointer in Rust and drop it appropriately?

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

Answers (1)

loganfsmyth
loganfsmyth

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

Related Questions