Can someone explain the error in the following code:
fn foo_normal(value: &mut i32) {}
fn foo_generic<T>(value: T) {}
fn main() {
let mut x = 10;
let mut_ref: &mut i32 = &mut x;
foo_normal(mut_ref);
foo_normal(mut_ref);
foo_generic(mut_ref); // <== Reborrow here to avoid error: change to: foo_generic(&mut *mut_ref)
foo_generic(mut_ref); // <== ERROR
println!("x: {}", x);
}
I believe I understand how reborrowing works:
The compiler converts foo_normal(mut_ref) to foo_normal(&mut *mut_ref), thus creating a temporary reference—otherwise, foo_normal would gain ownership over mut_ref.
But I don't understand the error for the generic function call.
When calling foo_generic, T is inferred to be &mut i32, so why don't we see the same behavior?
As far as I understand, T is deduced before type coercion is checked, so why must we explicitly create a temporary reference?
Remember that the lifetime is part of the type of the reference — therefore, when T is deduced, that includes a choice of lifetime, and the lifetime rustc chooses is the one that's already in the type of mut_ref — its entire remaining life. Arguably it should do something more flexible there, but it doesn’t.
Reborrowing isn't well documented,[1] but I think it works the same as let statements: you only get a coercion as specific as the annotation.
let mut local = ();
let borrow = &mut local;
// Moves
// let _this = borrow;
// let _this: _ = borrow; // <-- similar to a generic T
// Reborrows
let _this: &mut _ = borrow;
drop(borrow);
pretty much not at all outside RFCs last I looked ↩︎
Thanks you for your help. I actually have a related question to your answer
Please check the following code:
fn main() {
let mut num = 10_u32;
let a = &mut num;
*a = 20;
let b = a;
*b = 30;
*a = 30; // This lines fails ( use of moved value: `a`)
println!("num = {}", num);
}
I understand why the code fails.
But the thing is that I can replace let b = a with let b : &mut u32 = a and then everything works.
I was wondering if you can explain the difference betwen let with type annotation and without.
I couldn't find anything specific in the docs except that let with type annotation is a cohesion site. But I still don't understand why it matters, and what exactly is the b in let b = a?
I believe the difference is that if type inference is needed, then reborrowing doesn't happen. Type inference happens for example when you do let a = b, or when you call a generic function (where its generic type parameter needs to be inferred). If instead you manually specify the type of the let or call a non-generic function then no type inference needs to happen. This allows the compiler to insert an implicit reborrow.
let with an annotation is a coercion site, yeah. And as far as I understand, that's when reborrowing kicks in. Even that is an incomplete bit of documentation: the &mut type constructor needs to be explicit in the annotation.
That's just a pile of words if you don't already understand what I mean, so here's another example:
Two of the three moving examples are annotated, just not annotated "enough".
If the core of the question is "why doesn't reborrowing kick in without the annotation anyway", I don't have an answer. It would make purposefully "discarding" a &mut _ impossible, I think (but I also think I've only done that in examples and borrow-checking experiments, not "real" code).