`move` confusion

When I try below code, error occurs:

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
}

After macro expansion, your code becomes:

fn main() {
    let s = &String::from("hello");

    {
        ::std::io::_print(::core::fmt::Arguments::new_v1(
            &["s = ", "\n"],
            &match (&*s,) {
                (arg0,) => [::core::fmt::ArgumentV1::new(
                    arg0,
                    ::core::fmt::Display::fmt,
                )],
            },
        ));
    };
}

You can see that println!() automatically borrows its parameters, so *s becomes &*s, which is just reborrowing s.

2 Likes

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.

2 Likes