1
fn func(s: *mut String, a: *mut i32) -> usize {
    println!("{}", unsafe { *s });
    println!("{}", unsafe { *a });

    unsafe { (*s).len() }
}

fn main() {
    let mut s = String::from("hello");
    let mut a = 10;

    func(&mut s, &mut a);
}

The above code fails with the error:

error[E0507]: cannot move out of dereference of raw pointer
 --> src/main.rs:2:29
  |
2 |     println!("{}", unsafe { *s });
  |                             ^^ cannot move out of dereference of raw pointer

Why does it happen for String and not for i32? Why is it complaining of a "move"?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
soupybionics
  • 4,200
  • 6
  • 31
  • 43
  • 5
    *Why is it complaining of a "move"* — if you don't know what a "move" means in Rust, you *probably shouldn't be using raw pointers yet*. Go back and re-read the entire introductory book [*The Rust Programming Language*](https://doc.rust-lang.org/book/second-edition/) – Shepmaster Dec 19 '17 at 04:39
  • You could use `std::ptr::read` to copy out the raw string data, but that can easily result in undefined behaviour. – CodesInChaos Dec 22 '17 at 16:27

1 Answers1

5

Why does it happen for String and not for i32?

The basic integral types (and in fact, many other types) in Rust implement the Copy trait. They have "copy semantics", not "move semantics". There is no change of ownership here... you're copying out the value. String does not implement the Copy trait and therefore this binding has "move semantics".

This is not unique to raw pointers nor does it have anything to do with their mutability. This example shows this can happen with immutable references:

fn func(s: &String, a: &i32) {
    let _x = *s;
    let _x = *a;
}

Why is it complaining of a "move"?

It does this because you're attempting to move ownership out of the unsafe block. As long as you're care-free about this then you need to contain the "move" within the unsafe block so the compiler just lets you shoot yourself in the foot. As such, if you restructure your code so as to not move outside of the unsafe block, the code will compile:

unsafe {
    println!("{}", *s);
}

Here it is running in the playground.

To re-iterate Shepmaster's point in the comment on your question though... if the term "move" sounds foreign to you then you should not be using raw pointers/unsafe blocks in the first place and should instead head back to the available documentation for Rust to understand the concept.. as it is a core one.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138
  • So there is a transfer of ownership when calling println!() ? I mean why it works for ` let x = String::from ("hello"); println!("{}", x); println!("{}", x); ` . I am bit confused why the second println!() succeeds then? Also could you please elaborate on "It does this because you're attempting to move ownership out of the unsafe block". Please bear with my dumb questions – soupybionics Dec 19 '17 at 06:27
  • 2
    @soupybionics `println!` doesn't take ownership [(in fact, it auto-references its arguments)](https://stackoverflow.com/questions/30450399/does-println-borrow-or-own-the-variable), but the value has to be moved from inside the block to outside. [Here's an example of the same problem without any `unsafe`.](https://play.rust-lang.org/?gist=4cf04e1a7aadb054bcc3b6ba64aa03fe&version=stable) The `{}` block forces a move by making the value `*s` into a temporary, just like the `unsafe` block did. `println!("{}", *s);` would work. (But so does `println!("{}", s)`.) – trent Dec 19 '17 at 12:39
  • @trentcl, thanks! I am still quite not following it .What is the difference between unsafe { *s; } and unsafe { println!("{}", *s ); } – soupybionics Dec 19 '17 at 18:14
  • 1
    @soupybionics The difference is that `println!` auto-references its arguments, so there's an implicit `&` in front of the `*s`. The first Q&A I linked explains this. – trent Dec 19 '17 at 21:42
  • Thanks a lot trentcl! I think I get the gist now. Basically, the code `let a = String::from("abc"); a; println("{}", a);` triggers movement (transfer of ownership), resulting in error at `println!` as expected. So in the case of `unsafe { *s; }`, there is a movement to temporary (transfer of ownership again), just like the "abc" example above. Nothing more. I hope I have understood it correctly :) – soupybionics Dec 21 '17 at 03:42
  • Small correction. Rather than saying "there is a movement", I feel it is apt to say it rather **does not** allow the movement, considering the errors `cannot move out of borrowed context` or `cannot move out of dereference of raw pointer` – soupybionics Dec 21 '17 at 05:01