It's a bit more nuanced than that. The reborrow works kinda-sorta like a coercion. Rust doesn't have a spec yet and the official documentation on reborrows is surprisingly lacking, so I can't give a citation on when exactly automatic reborrows happen or don't happen. But we can get some approximation by looking at where coercions happen.
The relevant part for passing a reference to a function is
Arguments for function calls
The value being coerced is the actual parameter, and it is coerced to the type of the formal parameter.
And this can matter when you have a generic formal parameter instead of a concrete type. Consider this example, where append
now takes a generic parameter by value:
fn main() {
let mut x = String::from("hello");
let y = &mut x;
append(y);
println!("{}", y)
}
fn append<N: NeedlesslyGeneric>(p0: N) {
p0.push_str(" world");
}
It errors because y
is moved again -- because the formal parameter wasn't a &mut String
, the automatic reborrow does not take place. But you can do a "manual" reborrow:
- append(y);
+ append(&mut *y);
And now it works.
Now that you've seen a manual reborrow solve some scenario, let's return to this code that errors because you moved y
:
fn main() {
let mut x = String::from("hello");
let y = &mut x;
let _z = y;
println!("{}", y)
}
Does a manual reborrow work here too? Yes, it does.
And now let me draw your attention to another coercion site:
let statements where an explicit type is given.
Because of that, this version also compiles.
fn main() {
let mut x = String::from("hello");
let y = &mut x;
let _z: &mut _ = y;
println!("{}", y)
}
So sometimes passing a reference reborrows and sometimes it doesn't; sometimes assigning a reference to another variable reborrows and sometimes it doesn't.
This can all feel quite overly complex when first learning about it, but in practice reborrows mostly happen when you need them to, and you don't really think about them outside of debugging some subset of borrow check errors. I.e. it mostly "just works", and I feel most newcomers don't even register "but &mut
isn't copy and I passed it to a function, so why can I still use it" as you did.