Consider the following code:
#![allow(dead_code)]
fn f (x: &mut i32) -> &mut i32 { x }
fn id<T>(x: T ) -> T { x }
fn main() {
let mut x = 10;
let m = &mut x;
f(m);
*m = 11;
}
Note that f
and id
are identical, except that id
replaces &mut i32
with
a type parameter.
As written, this code compiles. Although m
is a &mut i32
, which is not
Copy
, the call to f
does not move the reference out of m
, because Rust
implicitly rewrites such calls to f(&mut *m)
. This allows the argument a lifetime
distinct from m
's, one which ends immediately after the call.
However, if you change f(m)
to id(m)
, the program no longer compiles: Rust
complains that the assignment to *m
is trying to use m
after it was moved,
and points out that &mut i32
is not Copy
.
Finally, if you change id(m)
to id::<&mut i32>
, the program does compile.
This was a surprise to me. My guess is that inferring id
's type parameter
unifies the T
with m
's type, so that T
has the same lifetime as m
, and
that Rust only introduces reborrows when the parameter and argument have
non-related lifetimes (the reborrow won't have any effect unless they do, anyway).