如何在任意生命周期内将 Rust 对象借给 C 代码?
How to lend a Rust object to C code for an arbitrary lifetime?
我正在用 Rust 编写一个具有 C 接口的库。 C 端必须能够创建和销毁 Rust 对象(C 端拥有它们并控制它们的生命周期)。
我已经设法 "leak" 一个 C 对象,但我不确定如何正确释放它:
pub extern "C" fn create() -> *mut Foo {
让 obj = Foo; // 哎呀,一个错误
让 ptr = std::mem::transmute(&mut obj); // 坏的
std::mem::forget(对象); // 没有必要
return ptr;
}
pub extern "C" fn destroy(handle: *mut Foo) {
// 取回 Foo 并丢弃它???
}
我不确定如何将指针转回 Rust 将调用 Drop 的对象。简单地取消引用 *handle
不会编译。
实际上,您还没有设法将对象泄露给 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()));
// into_raw turns the Box into a *mut Dramatic, which the borrow checker
// ignores, without calling its destructor.
Box::into_raw(obj)
}
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. from_raw is the
// inverse of into_raw: it turns the *mut Dramatic back into a
// Box<Dramatic>. You must only call from_raw once per pointer.
let obj: Box<Dramatic> = unsafe { Box::from_raw(*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.
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);
}
发送 Rust object 到 C:
#[no_mangle]
pub extern "C" fn create_foo() -> *mut Foo {
Box::into_raw(Box::new(Foo))
}
或者利用 Box
是 FFI-safe 并且与指针相同,并且 Rust 函数定义不必完全匹配 C headers 的事实,只要ABI 相同:
#[no_mangle]
pub extern "C" fn create_foo() -> Box<Foo> {
Box::new(Foo)
}
(返回 Option<Box<Foo>>
也可以。Result
不行。)
从 C:
借用(而不是免费)
#[no_mangle]
pub unsafe extern "C" fn peek_at(foo: *mut Foo) {
let foo = foo.as_ref().unwrap(); // That's ptr::as_ref
}
或利用引用和 Option
成为 FFI-safe:
#[no_mangle]
pub extern "C" fn peek_at(foo: Option<&mut Foo>) {
let foo = foo.unwrap();
}
取 over/destroy Rust object 以前给 C:
#[no_mangle]
pub unsafe extern "C" fn free_foo(foo: *mut Foo) {
assert!(!foo.is_null());
Box::from_raw(foo); // Rust auto-drops it
}
或者使用 Rust 的 Option<Box>
是 FFI-safe 和 memory-managed 的事实:
#[no_mangle]
pub unsafe extern "C" fn free_foo(foo: Option<Box<Foo>>) {
// dropped implicitly
}
我正在用 Rust 编写一个具有 C 接口的库。 C 端必须能够创建和销毁 Rust 对象(C 端拥有它们并控制它们的生命周期)。
我已经设法 "leak" 一个 C 对象,但我不确定如何正确释放它:
pub extern "C" fn create() -> *mut Foo { 让 obj = Foo; // 哎呀,一个错误 让 ptr = std::mem::transmute(&mut obj); // 坏的 std::mem::forget(对象); // 没有必要 return ptr; } pub extern "C" fn destroy(handle: *mut Foo) { // 取回 Foo 并丢弃它??? }
我不确定如何将指针转回 Rust 将调用 Drop 的对象。简单地取消引用 *handle
不会编译。
实际上,您还没有设法将对象泄露给 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()));
// into_raw turns the Box into a *mut Dramatic, which the borrow checker
// ignores, without calling its destructor.
Box::into_raw(obj)
}
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. from_raw is the
// inverse of into_raw: it turns the *mut Dramatic back into a
// Box<Dramatic>. You must only call from_raw once per pointer.
let obj: Box<Dramatic> = unsafe { Box::from_raw(*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.
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);
}
发送 Rust object 到 C:
#[no_mangle] pub extern "C" fn create_foo() -> *mut Foo { Box::into_raw(Box::new(Foo)) }
或者利用 Box
是 FFI-safe 并且与指针相同,并且 Rust 函数定义不必完全匹配 C headers 的事实,只要ABI 相同:
#[no_mangle]
pub extern "C" fn create_foo() -> Box<Foo> {
Box::new(Foo)
}
(返回 Option<Box<Foo>>
也可以。Result
不行。)
从 C:
借用(而不是免费)#[no_mangle] pub unsafe extern "C" fn peek_at(foo: *mut Foo) { let foo = foo.as_ref().unwrap(); // That's ptr::as_ref }
或利用引用和 Option
成为 FFI-safe:
#[no_mangle]
pub extern "C" fn peek_at(foo: Option<&mut Foo>) {
let foo = foo.unwrap();
}
取 over/destroy Rust object 以前给 C:
#[no_mangle] pub unsafe extern "C" fn free_foo(foo: *mut Foo) { assert!(!foo.is_null()); Box::from_raw(foo); // Rust auto-drops it }
或者使用 Rust 的 Option<Box>
是 FFI-safe 和 memory-managed 的事实:
#[no_mangle]
pub unsafe extern "C" fn free_foo(foo: Option<Box<Foo>>) {
// dropped implicitly
}