我正在Rust中编写一个具有C接口的库.C方必须能够创建和销毁Rust对象(C方拥有它们并控制它们的生命周期).
我设法将一个对象"泄漏"到C,但我不确定如何正确释放它:
pub extern "C" fn create() -> *mut Foo {
let obj = Foo; // oops, a bug
let ptr = std::mem::transmute(&mut obj); // bad
std::mem::forget(obj); // not needed
return ptr;
}
pub extern "C" fn destroy(handle: *mut Foo) {
// get Foo back and Drop it???
}
我不知道如何将指针转回Rust会调用Drop的对象.简单地解除引用*handle不会编译.
要将Rust对象发送到C:
#[no_mangle]
pub extern "C" fn create_foo() -> *mut Foo {
Box::into_raw(Box::new(Foo))
}
借用C(而不是免费):
#[no_mangle]
pub unsafe extern "C" fn do(foo: *mut Foo) -> *mut Foo {
let foo = foo.as_ref().unwrap(); // That's ptr::as_ref
}
接管/销毁以前给C的Rust对象:
#[no_mangle]
pub unsafe extern "C" fn free_foo(foo: *mut Foo) {
assert!(!foo.is_null());
Box::from_raw(foo); // Rust auto-drops it
}
实际上,你没有设法将对象泄漏到C; 你已设法泄漏对(不久)不存在的堆栈帧的引用.:d
这是一个应该正常工作的完整示例.我试图用它来评论它,以解释我在做什么以及为什么.
pub struct Dramatic(String);
// Implement a destructor just so we can see when the object is destroyed.
impl Drop for Dramatic {
fn drop(&mut self) {
println!("And lo, I, {}, meet a most terrible fate!", self.0);
}
}
pub extern "C" fn create() -> *mut Dramatic {
// We **must** heap-allocate the object! Returning a reference to a local
// will **almost certainly** break your program!
let mut obj = Box::new(Dramatic("Roger".to_string()));
// * derefs the Box into a Dramatic, the &mut re-borrows it into a regular
// reference. The constraint ensures we coerce the &mut Dramatic into
// a *mut Dramatic, which "hides" the reference from the borrow checker.
let ptr: *mut _ = &mut *obj;
// Forget discards its argument (passed by-move), without trigger its
// destructor, if it has one.
::std::mem::forget(obj);
ptr
}
pub extern "C" fn destroy(ptr: &mut *mut Dramatic) {
// First, we **must** check to see if the pointer is null.
if ptr.is_null() {
// Do nothing.
return;
}
// Now, we know the pointer is non-null, we can continue.
let obj: Box<Dramatic> = unsafe { ::std::mem::transmute(*ptr) };
// We don't *have* to do anything else; once obj goes out of scope, it will
// be dropped. I'm going to drop it explicitly, however, for clarity.
::std::mem::drop(obj);
// I am, however, going to null out the `ptr` we were passed just so the
// calling code is less likely to accidentally re-use the pointer.
*ptr = ::std::ptr::null_mut();
}
fn main() {
let mut ptr = create();
println!("ptr = {:?}", ptr);
destroy(&mut ptr);
println!("ptr = {:?}", ptr);
}
Run Code Online (Sandbox Code Playgroud)