First of all, I'm not asking what's the difference between &mut
and ref mut
per se.
I'm asking because I thought:
let ref mut a = MyStruct
is the same as
let a = &mut MyStruct
Consider returning a trait object from a function. You can return a Box<Trait>
or a &Trait
. If you want to have mutable access to its methods, is it possible to return &mut Trait
?
Given this example:
trait Hello {
fn hello(&mut self);
}
struct English;
struct Spanish;
impl Hello for English {
fn hello(&mut self) {
println!("Hello!");
}
}
impl Hello for Spanish {
fn hello(&mut self) {
println!("Hola!");
}
}
The method receives a mutable reference for demonstration purposes.
This won't compile:
fn make_hello<'a>() -> &'a mut Hello {
&mut English
}
nor this:
fn make_hello<'a>() -> &'a mut Hello {
let b = &mut English;
b
}
But this will compile and work:
fn make_hello<'a>() -> &'a mut Hello {
let ref mut b = English;
b
}
My theory
This example will work out of the box with immutable references (not necessary to assign it to a variable, just return &English
) but not with mutable references. I think this is due to the rule that there can be only one mutable reference or as many immutable as you want.
In the case of immutable references, you are creating an object and borrowing it as a return expression; its reference won't die because it's being borrowed.
In the case of mutable references, if you try to create an object and borrow it mutably as a return expression you have two mutable references (the created object and its mutable reference). Since you cannot have two mutable references to the same object it won't perform the second, hence the variable won't live long enough. I think that when you write let mut ref b = English
and return b
you are moving the mutable reference because it was captured by a pattern.
All of the above is a poor attempt to explain to myself why it works, but I don't have the fundamentals to prove it.
Why does this happen?
I've also cross-posted this question to Reddit.
This is a bug. My original analysis below completely ignored the fact that it was returning a mutable reference. The bits about promotion only make sense in the context of immutable values.
This is allowable due to a nuance of the rules governing temporaries (emphasis mine):
When using an rvalue in most lvalue contexts, a temporary unnamed lvalue is created and used instead, if not promoted to
'static
.
The reference continues:
Promotion of an rvalue expression to a
'static
slot occurs when the expression could be written in a constant, borrowed, and dereferencing that borrow where the expression was the originally written, without changing the runtime behavior. That is, the promoted expression can be evaluated at compile-time and the resulting value does not contain interior mutability or destructors (these properties are determined based on the value where possible, e.g.&None
always has the type&'static Option<_>
, as it contains nothing disallowed).
Your third case can be rewritten as this to "prove" that the 'static
promotion is occurring:
fn make_hello_3<'a>() -> &'a mut Hello {
let ref mut b = English;
let c: &'static mut Hello = b;
c
}
As for why ref mut
allows this and &mut
doesn't, my best guess is that the 'static
promotion is on a best-effort basis and &mut
just isn't caught by whatever checks are present. You could probably look for or file an issue describing the situation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With