fn main() {
let s = &String::from("hello");
let p = *s; // move occurs because `*s` has type `String`,
// which does not implement the `Copy` trait help:
// consider borrowing here: `&*s`
println!("p = {}",p);
}
but if I put *s to println!(), it works:
fn main() {
let s = &String::from("hello");
println!("s = {}", *s); // output: s = hello
}
Another thing that’ll work is *s == *s. The situation is the following: *s in and by itself only refers to the place of the String in memory directly, but just writing *s doesn’t immediately try to move out of it. Something like your assignment
let p = *s;
does a move (so it fails to compile with a String), but something like
let p = &*s;
or equivalently
let ref p = *s;
forks fine and doesn’t move anything. These kinds of “place expressions”, or in C++ terminology sometimes called “lvalue expression” are admittedly a bit weird because of how you can’t use them in certain ways but you can in others. One other example of what *s can do (if it’s behind a mutable reference): be the target of assignment, e.g.
let s = &mut String::from("hello");
*s = String::from("bye");
It’s appearance on the left-hand-side of an = is why they’re sometimes called “lvalue” (that’s an “l” for “left”).
You can’t pass a place expression to a function, i.e. calling some_function(*s) will always try to move out of *s, so they are not quite a fist-class citizen of the Rust language in this way. Some overloadable operations such as == will solve this problem by desugaring to something that creates a reference, i.e. foo == bar will become PartialEq::eq(&foo, &bar). Similarly println!("{}", foo) does desugar, as explained above, to something involving &foo.