&mut T seems to behave like a Copy type

I am trying to better understand move semantics and the behavior of Copy types in Rust.

I know that &mut T is not Copy, as explained here, because copying &mut T would create an aliased mutable reference. But then I do not understand the behavior of the following program:

fn my_ref_mut(the_ref_mut: &mut Vec<i32>) {
    println!("{:?}", the_ref_mut);
}
fn main() {
    let mut v: Vec<i32> = Vec::new();
    let r: &mut Vec<i32> = &mut v;
    my_ref_mut(r);
    my_ref_mut(r); // Should not this be an `use after move` error?
}

I would have expected the program above not to compile because of an E0382: use of moved value error. The r variable behaves as if it is Copy. I have tried to better understand the situation with the following program. It gives two different compilation errors, depending on whether the types are explicitly specified (for me to be sure I get the expected types):

fn my_ref_mut(the_ref_mut: &mut Vec<i32>) {
println!("{:?}", the_ref_mut);
}

fn my_ref(the_ref: &Vec<i32>) {
    println!("{:?}", the_ref);
}

fn main() {
    let mut v: Vec<i32> = Vec::new();
    {
        let r: &mut Vec<i32> = &mut v;
        my_ref_mut(r);
        my_ref_mut(r);
    }
    {
        let r1: &mut Vec<i32> = &mut v;
        my_ref_mut(r1);
        let r2 = r1;
        my_ref_mut(r1);
    }
    {
        let r: &Vec<i32> = &v;
        my_ref(r);
        my_ref(r);
    }
    {
        let r1: &Vec<i32> = &v;
        my_ref(r1);
        let r2: &Vec<i32> = r1;
        my_ref(r1);
    }
}

The error is self explanatory:

error[E0382]: use of moved value: `*r1`
  --> src/main.rs:20:20
   |
19 |         let r2 = r1;
   |             -- value moved here
20 |         my_ref_mut(r1);
   |                    ^^ value used here after move
   |
   = note: move occurs because `r1` has type `&mut std::vec::Vec<i32>`, which does not implement the `Copy` trait

I would have expected it to be use of moved value: r1, not *r1, because I have moved the reference, not the value itself.

By changing line 19 to let r2: &mut Vec<i32> = r1;, thus specifying the type of r2 explicitly, the error becomes E0499:

error[E0499]: cannot borrow `*r1` as mutable more than once at a time
  --> src/main.rs:20:20
   |
19 |         let r2: &mut Vec<i32> = r1;
   |                                 -- first mutable borrow occurs here
20 |         my_ref_mut(r1);
   |                    ^^ second mutable borrow occurs here
21 |     }
   |     - first borrow ends here

This error also seems wrong to me, because the first borrow of *r1 should be the one on line 17 (let r1: &mut Vec<i32> = &mut v;). I know about autoref and Deref coercion, but from what I believe, they are not applicable here.

My conclusion is that &T is Copy, as expected and as the second example above demonstrates. But I cannot say the same thing about &mut T. I would like someone to explain to me the compilation errors and why &mut T sometimes seems to behave like being Copy. Is it also possible for this to be a compiler bug? My toolchain is:

stable-x86_64-unknown-linux-gnu (default)
rustc 1.25.0 (84203cac6 2018-03-25)
2 Likes

Yes, &T is Copy. It's technically implemented as a pointer, so it's copied whenever needed and it doesn't move the T.

&mut T is similar. Although formally it doesn't implement Copy, it's made to behave as close to Copy as possible. I think the magic behind it is "reborrowing" and the borrow checker will make you another &mut T whenever it's needed and safe.

4 Likes

I think you're running into "reborrowing"; &T is Copy, &mut T is not, but sometimes rustc will create a temporary &mut T to the contents of a &mut T, which can make it feel Copy.

6 Likes

Yeah, those &mut T cases are reborrows. A good way to observe a move of a mutable reference is to use generics; reborrow does not take place in a generic context. So eg:

fn my_ref_mut<T: Debug>(the_ref_mut: T) {
    println!("{:?}", the_ref_mut);
}
fn main() {
    let mut v: Vec<i32> = Vec::new();
    let r: &mut Vec<i32> = &mut v;
    my_ref_mut(r); // will be moved here
    my_ref_mut(r); // this is now an error
}
6 Likes

Your example is very interesting. I was expecting it to successfully compile too. Is this the expected behavior that usage of generics will not allow "reborrowing"? It seems like a bug to me, as I have no idea about how the Rust compiler works. There are many subtle differences like this which make learning Rust a bit more difficult than it already is.

Now I know that reborrowing is the explanation for my problem. But are the compilation errors correct? I still believe the first one should be: use of moved value r1, not *r1. I also believe that the second error should have also been a use of moved value error, not the one it it is now. Again, I do not know how the compiler works, but I believe that adding a specified type (which is the same as the inferred type, as far as I know) should not change the error the compiler reports (maybe report a different error, but a correct one).

1 Like

I don’t believe it’s considered a bug. You can do a manual reborrow via &mut *r and then it works again.

I agree that Rust has quite a bit of subtlety in some areas. Hopefully the number of them will decrease over time.

I agree - I’m not sure where the * comes from in the message. It might be some artifact of compiler attempting a reborrow and failing. The elaborated error message talks about r1, vs *r1, and is more appropriate.

The second case is I think type ascription influencing compiler’s decision on move vs reborrow; the second case, with the type ascribed, appears to make compiler perform a reborrow. In the first case, it just moves. Not sure why that is but a guess would be that let r2: &mut Vec<i32> creates a new region for r2 (ie the implicit lifetime of this borrow), and that somehow influences what the compiler does.

1 Like