這似乎工作,但它確實需要使用一個小的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);
}
這可以防止當使用指針變量是有效的超越基準,這是不保證是真終身,但在許多情況下「足夠接近」。太短暫,不要太長時間更重要!
我問IRC,看來答案是:是的,這是可能的,但它會變得更好https://github.com/rust-lang/rfcs/pull/1861 – derekdreery