2017-03-05 63 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); 
} 

這可以防止當使用指針變量是有效的超越基準,這是保證是真終身,但在許多情況下「足夠接近」。太短暫,不要太長時間更重要!