这似乎工作,但它确实需要使用一个小的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