Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it allowed to reassign String but not *&String

Tags:

rust

ownership

I understand String doesn't implement the Copy trait. So it cannot be moved. But I am failing to understand the difference between the following,

let greeting = String::from("Hello");
let temp = greeting; // This succeeds.

let greeting1 = String::from("Hello");
let temp1 = *&greeting1; // This fails to compile saying cannot move. 

What is the difference between them ? Why one is valid and another is not?

like image 540
mybloodtop Avatar asked Dec 17 '25 12:12

mybloodtop


2 Answers

I understand String doesn't implement the Copy trait. So it cannot be moved.

This is not really correct - values of all types can be moved, but you need to be the owner in order to move something. For example, if you write a function whose return type is String, when you return a value from this function, you are moving it (to the caller).

Copy is related to this in that a type implementing Copy allows you to implicitly convert a shared reference into an owned copy, which you are then allowed to move (since you own it).

What is the difference between them ? Why one is valid and another is not?

&greeting1 creates a shared reference to the string; the String value is not owned by the reference. So *&greeting1 is a value of type String that you don't own, and you're not allowed to do anything with it that would require ownership.

like image 76
kaya3 Avatar answered Dec 19 '25 05:12

kaya3


You can't move out of references, and if you could, you would definitely not be able to move out of a shared reference because moving is a mutating operation. So the code you should be testing is let temp1 = *&mut greeting1;, but this won't work either.

Moving out of references isn't supported because moving requires the compiler to be able to statically determine that the moved-from location is no longer used, and when references are in play the moved-from location isn't known -- the reference obfuscates this.

Consider:

fn foo(s: &mut String) {
    let x = *s;
}

fn bar() -> String {
    let s = String::new();
    foo(&mut s);
    s // What happens here?
}

How is bar() to know that foo() is going to steal the s value? It can't know this. When it attempts to use s after it was moved, there would be undefined behavior.

To prevent this from happening, only the owner of a value is allowed to move it. A reference does not own a value; therefore moving out of a reference is forbidden.

Note that you can "steal" the value behind a mutable reference, but only if you have something to leave in its place. For example, the built-in std::mem::take() function will steal the value behind a mutable reference, but it requires that the type of the value being stolen implements Default, and it leaves the default value of the type in its place. This way, you can move the value out of the reference, and the referent is still a valid value.

In your code, this would look like:

let temp1 = std::mem::take(&mut greeting1);

After this line, temp1 will contain the value greeting1 used to contain, and greeting1 will be set to <String as Default>::default() (which would be an empty string).

like image 21
cdhowie Avatar answered Dec 19 '25 07:12

cdhowie



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!