2017-03-05 53 views
3

c_strange_t是一个不透明的C类型,只能在指针后面看到。在封装这种类型时,有时我们有责任通过使用c_free_strange_t(*c_strange_t)来释放内存,有时我们不负责释放数据,我们只负责精确控制生命周期。如何处理可能拥有或借用的FFI未分类类型?

如果这种类型可以被映射到2种拉斯特以类似的方式工作,以strString,那里是impl Deref<Target=str> for String这将是符合人体工程学。借用的类型需要被标记为仅在参考后有效。

这是可能的,它将如何完成?

+0

我问IRC,看来答案是:是的,这是可能的,但它会变得更好https://github.com/rust-lang/rfcs/pull/1861 – derekdreery

回答

2

这似乎工作,但它确实需要使用一个小的unsafe块,所以你应该使用像Valgrind这样的常规工具进行测试。这里所做的主要假设是c_void不能正常构建,并且FooBorrowed newtype不会增加开销。一切都应该结束了作为“只是一个指针”:

extern crate libc; 

use std::ops::Deref; 
use std::mem; 

struct FooBorrowed(libc::c_void); 
struct FooOwned(*const libc::c_void); 

fn fake_foo_new() -> *const libc::c_void { 
    println!("C new called"); 
    std::ptr::null() 
} 

fn fake_foo_free(p: *const libc::c_void) { 
    println!("C free called");  
    assert!(p.is_null()); 
} 

fn fake_foo_value(p: *const libc::c_void) -> u8 { 
    println!("C value called");  
    assert!(p.is_null()); 
    42 
} 

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

impl FooOwned { 
    fn new() -> FooOwned { 
     FooOwned(fake_foo_new()) 
    } 
} 

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(); 
    use_it(&f); 
} 

如果C库实际上是给了你一个指针,你需要做一些更多的unsafe

fn fake_foo_borrowed() -> *const libc::c_void { 
    println!("C borrow called"); 
    std::ptr::null() 
} 

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

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

当你确定,FooBorrowed::new回报提供无限制的使用期限。这很危险。在许多情况下,可以构造一个较小的范围和使用的东西,它提供了一生:

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

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

这可以防止当使用指针变量是有效的超越基准,这是保证是真终身,但在许多情况下“足够接近”。太短暂,不要太长时间更重要!