你能在Rust中将一个不安全指针转换为拥有的指针吗?

3

我知道这可能是一个比较晦涩的要求,但有时候通过ffi在c库中存储一个值并从c回调到rust函数会很有帮助。

在这些情况下,我发现我想要拥有一个作为mut * c_void存储的拥有指针。

你可以使用forget()方法使用这种方法将值存储在C中,并且你可以相对容易地将其复活为mut * T,但在这种情况下,能够从不安全指针移出并返回到“安全”的rust空间非常有用。

然而,编译器总是会抱怨将引用从&指针移出。

是否有一种特殊的方法来避免这种情况,将&T或*T不安全地转换为~T?

这可能有点不清楚,所以这里有一个具体的例子:

extern crate libc;

use libc::c_void;
use std::ops::Drop;
use std::intrinsics::forget;

struct Foo { x: int }

impl Drop for Foo {
  fn drop(&mut self) {
    println!("We discarded our foo thanks!");
  }
}

fn main() {
  let mut x = ~Foo { x: 10 };

  let mut y = & mut *x as * mut Foo;
  println!("Address Y: {:x}", y as uint);

  let mut z = y as * mut c_void;
  println!("Address Z: {:x}", z as uint);

  // Forget x so we don't worry about it
  unsafe { forget(x); }

  // This would normally happen inside the ffi callback where 
  // the ffi code discards the void * it was holding and returns it.
  unsafe {
    let mut res_z = z as * mut Foo;
    println!("Ressurected Z: {:x}", z as uint);

    let mut res_y = & mut (*res_z);
    println!("Ressurected Y: {:x}", y as uint);

    let mut res_x:~Foo = ~(*res_y);
  }
}

那个最后的行无法编译,因为:

test.rs:34:28: 34:34 error: cannot move out of dereference of `&mut`-pointer
test.rs:34     let mut res_x:~Foo = ~(*res_y);

如果您删除该行,则析构函数将永远不会被调用;我正在寻找一种方法将该内存块移回“安全”的 Rust 区域,并在块作用域结束时正确地进行收集。 编辑:我怀疑 swap() 是这样做的方法,但是正确执行它的确切语义对我来说仍然有点模糊。例如,在 https://gist.github.com/shadowmint/11361488 中,一个 ~Foo 泄漏了;忘记的 ~Foo 的析构函数被调用,但是我们泄漏了在 tmp 中创建的本地 ~Foo。 :/
2个回答

2
你的错误是因为~是一个装箱操作符,而不是获取指针。你不能“获取”一个拥有指针的东西,因为创建一个拥有指针总是意味着在堆上分配。因此出现了错误 - ~试图移动&mut中的值,这是不允许的。
我认为对于这种真正不安全的事情,你需要使用cast::transmute()
use std::cast;

// ...

let mut res_x: ~Foo = cast::transmute(res_y);

通过这个修改,你的代码可以正常工作,并且打印出We discarded our foo thanks!。但是,你应该非常小心使用cast::transmute(),因为它完全没有检查(除了转换类型的内存大小),并且允许你将任何东西转换成任何东西。


0

否则我认为你需要编写另一个函数来调用res_y的析构函数,以便在外部访问。 - santu47
在这种情况下,res_y 是一个指向 mut T 的引用,我想要将其“提升”为 ~T。*res_y 确实是一个 T,但我想以某种方式将其转换为拥有的指针。 - Doug

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接