4 ms·
I like using this neat newtype for wrapping FFI objects. pub struct OwnedForeignInstance(NonNull<std::ffi::c_void>); You can then, as you say, implement Dro
by arcticbull 3y ago
I like using this neat newtype for wrapping FFI objects.
pub struct OwnedForeignInstance(NonNull<std::ffi::c_void>);
You can then, as you say, implement Drop to give up the foreign object when it's no longer needed.
impl Drop for OwnedForeignInstance {
fn drop(&mut self) {
unsafe { /* Destroy self.0.as_ptr() */ }
}
}
You can define an un-owned reference to the owned foreign instance using a newtype like this.
pub struct UnownedReference(PhantomData<UnsafeCell<*mut ()>>);
Then, you can hand out zero-cost lifetime-checked references to the owned foreign instance like this.
impl Deref for OwnedForeignInstance {
type Target = UnownedReference;
fn deref(&self) -> &Self::Target {
unsafe { &*(self.0.as_ptr() as *mut _) }
}
}
impl DerefMut for OwnedForeignInstance {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self.0.as_ptr() as *mut _) }
}
}
Once you've done that, you expose your FFI functionality on UnownedReference, relying on auto-deref. Unless it consumes the receiver, in which case you put it on the OwnedForeignInstance. This way you can't destroy the object while references to it continue to exist.
It's not perfect, but it's the best way I've found so far for making FFI wrapper objects that look and feel like Rust objects while respecting the FFI contract.