Can you convert an unsafe pointer to an owned pointer in rust somehow?

StackOverflow https://stackoverflow.com/questions/23331808

  •  10-07-2023
  •  | 
  •  

سؤال

I realize this is something of an obscure thing to want to do, but sometimes it's somewhat helpful to be able to store a value in a c library via ffi, and then make a callback to a rust function from c.

In these circumstances I've found situations where I want to have an owned pointer which is stored as a mut * c_void.

You can store a value in C using this method along with forget(), and you can relatively easily resurrect it as a mut * T on the other side, but it's useful to be able to move out of the unsafe pointer and back into 'safe' rust space in this situation.

However, the compiler will always complain about moving the reference out of a & pointer.

Is there a special way around that, to unsafely convert a &T or *T into a ~T?

That may be a little unclear, so here's a tangible example:

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);
  }
}

That final line won't compile because:

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);

If you remove that line the destructor is never invoked; I'm looking for a way to move that memory block back into the 'safe' rust zone and have it collected correctly when the block scope ends.

edit: I suspect swap() is the way to do this, but the exact semantics of doing it right without leaking are still a bit obscure to me. For example in https://gist.github.com/shadowmint/11361488 a ~Foo leaks; the destructor is invoked for the forgotten ~Foo, but instead we leak the local ~Foo created in tmp. :/

هل كانت مفيدة؟

المحلول

Your error happens because ~ is an operator for boxing, not taking a pointer. You can't "take" an owned pointer to something, because creating an owned pointer always means allocation on the heap. Hence your error - ~ tries to move the value out of &mut, which is disallowed.

I believe that for such kind of really unsafe things you'll need cast::transmute():

use std::cast;

// ...

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

Your code with this modification works fine and it prints We discarded our foo thanks!. However, you shoud really be careful with cast::transmute(), because it does completely no checks (except memory size of converted types) and will allow you to cast everything to everything.

نصائح أخرى

Is the syntax correct? the line should look like as below, if I'm not wrong.

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

I'm a newbie to rust and I just followed this [link]: http://cmr.github.io/blog/2013/08/13/rust-by-concept/

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top