如何处理可能拥有或借用的FFI超大字体? [英] How do I handle an FFI unsized type that could be owned or borrowed?

查看:73
本文介绍了如何处理可能拥有或借用的FFI超大字体?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

c_strange_t是不透明的C类型,仅在指针后面可见.在包装此类型时,有时我们有责任使用c_free_strange_t(*c_strange_t)释放内存,而有时我们不负责释放数据,而仅负责精确地控制生命周期.

c_strange_t is an opaque C type that is only seen behind a pointer. When wrapping this type, there are times when it is our responsibility to free memory using c_free_strange_t(*c_strange_t), and other times when we are not responsible for freeing the data, we are only responsible for accurately controlling the lifetime.

如果可以在Rust中将此类型映射为2种类型,而这些类型的工作方式与strString(其中有impl Deref<Target=str> for String)相似,则将符合人体工程学.借用的类型将需要标记为仅在引用后面有效.

It would be ergonomic if this type could be mapped into 2 types in Rust that work in a similar way to str and String, where there is impl Deref<Target=str> for String. The borrowed type would need to be marked as only valid behind a reference.

这可能吗,怎么办?

推荐答案

这似乎可行,但是确实需要使用一个小的unsafe块,因此您应该在Miri和Valgrind等常规工具下进行测试.此处 1 的主要假设是c_void无法正常构造. #[repr(transparent)] 用于确保FooBorrowed新型与c_void具有相同的内存布局.一切都应该以只是指针"结尾:

This appears to work, but it does require using a small unsafe block, so you should test under the normal tools like Miri and Valgrind. The primary assumption made here1 is that c_void cannot be constructed normally. #[repr(transparent)] is used to ensure that the FooBorrowed newtype has the same memory layout as a c_void. Everything should end up as "just a pointer":

use std::{ffi::c_void, mem, ops::Deref};

#[repr(transparent)]
struct FooBorrowed(c_void);
struct FooOwned(*mut c_void);

fn fake_foo_new(v: u8) -> *mut c_void {
    println!("C new called");
    Box::into_raw(Box::new(v)) as *mut c_void
}

fn fake_foo_free(p: *mut c_void) {
    println!("C free called");
    let p = p as *mut u8;
    if !p.is_null() {
        unsafe { Box::from_raw(p) };
    }
}

fn fake_foo_value(p: *const c_void) -> u8 {
    println!("C value called");
    let p = p as *const u8;
    unsafe {
        p.as_ref().map_or(255, |p| *p)
    }
}

impl FooBorrowed {
    fn value(&self) -> u8 {
        fake_foo_value(&self.0)
    }
}

impl FooOwned {
    fn new(v: u8) -> FooOwned {
        FooOwned(fake_foo_new(v))
    }
}

impl Deref for FooOwned {
    type Target = FooBorrowed;

    fn deref(&self) -> &Self::Target {
        unsafe { mem::transmute(self.0) }
    }
}

impl Drop for FooOwned {
    fn drop(&mut self) {
        fake_foo_free(self.0)
    }
}

fn use_it(foo: &FooBorrowed) {
    println!("{}", foo.value())
}

fn main() {
    let f = FooOwned::new(42);
    use_it(&f);
}

如果C库实际上为您提供了一个指针,则您需要做更多的工作unsafe:

If the C library actually hands you a pointer, you would need to do some more unsafe:

fn fake_foo_borrowed() -> *const c_void {
    println!("C borrow called");
    static VALUE_OWNED_ELSEWHERE: u8 = 99;
    &VALUE_OWNED_ELSEWHERE as *const u8 as *const c_void
}

impl FooBorrowed {
    unsafe fn new<'a>(p: *const c_void) -> &'a FooBorrowed {
        mem::transmute(p)
    }
}

fn main() {
    let f2 = unsafe { FooBorrowed::new(fake_foo_borrowed()) };
    use_it(f2);
}

正如您所确定的,FooBorrowed::new返回一个寿命不受限制的引用.这是非常危险的.在许多情况下,您可以构造较小的范围并使用可以延长使用寿命的东西:

As you identified, FooBorrowed::new returns a reference with an unrestricted lifetime; this is pretty dangerous. In many cases, you can construct a smaller scope and use something that provides a lifetime:

impl FooBorrowed {
    unsafe fn new<'a>(p: &'a *const c_void) -> &'a FooBorrowed {
        mem::transmute(*p)
    }
}

fn main() {
    let p = fake_foo_borrowed();
    let f2 = unsafe { FooBorrowed::new(&p) };
    use_it(f2);
}

这将防止您在指针变量有效时使用引用,该变量不能保证是真实的生命周期,但在许多情况下足够接近".太短而不能太长更重要!

This prevents you from using the reference beyond when the pointer variable is valid, which is not guaranteed to be the true lifetime, but is "close enough" in many cases. It's more important to be too short and not too long!

1 —在Rust的未来版本中,您应使用外部类型创建有保证的不透明类型:

1 — In future versions of Rust, you should use extern types to create a guaranteed opaque type:

extern "C" {
    type my_opaque_t;
}

这篇关于如何处理可能拥有或借用的FFI超大字体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆