Why does the Rust compiler reject this implementation of get? Because it allows:
The following is a perfectly reasonable main, assuming that get compiles:
fn main() {
let mut x = 3;
let y = Foo { x: &mut x };
let a = y.get();
let b = y.x;
println!("{} {}", a, b);
}
Yet if get were to compile, this would be fine:
a does not borrow y because the lifetime is different
b "consumes" y (moving from y.x) but we do not reuse it after
So everything is fine, except that we now have a &i32 and &mut i32 both pointing to x.
Note: to make it compile, you can use unsafe inside of get: unsafe { std::mem::transmute(&*self.x) }; scary, eh?
At the heart of the borrow-checking algorithm is the cornerstone on which Rust's memory safety is built:
Aliasing XOR Mutability
Rust achieves memory safety without garbage collection by guaranteeing that whenever you are modifying something, no observer can have a reference inside that something that could become dangling.
This, in turns, lets us interpret:
&T as an aliasing reference; it is Copy
&mut T as a unique reference; it is NOT Copy, as it would violate uniqueness, but it can be moved
This difference saved us here.
Since &mut T cannot be copied, the only way to go from &mut T to &T (or &mut T) is to perform a re-borrowing: dereference and take a reference to the result.
This is done implicitly by the compiler. Doing it manually makes for a somewhat better error message:
fn get<'b>(&'b self) -> &'a i32 {
&*self.x
}
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> <anon>:7:9
|
7 | &*self.x
| ^^^^^^^^
|
help: consider using an explicit lifetime parameter as shown: fn get(&'a self) -> &'a i32
--> <anon>:6:5
|
6 | fn get<'b>(&'b self) -> &'a i32 {
| ^
Why cannot it infer a lifetime? Because lifetime of the re-borrow is limited by 'b but we are requiring a 'a and there's no relationship between the two!
By the way, this is what is saving us from blunder here, because it ensures that the instance Foo must be borrowed while the result lives (preventing us to use a mutable reference via Foo::x).
Following the compiler hint, and returning &'b i32 works... and prevents the above main from compiling:
impl<'a> Foo<'a> {
fn get<'b>(&'b self) -> &'b i32 {
&*self.x
}
}
fn main() {
let mut x = 3;
let y = Foo { x: &mut x };
let a = y.get();
let b = y.x;
println!("{} {}", a, b);
}
error[E0505]: cannot move out of `y.x` because it is borrowed
--> <anon>:16:9
|
15 | let a = y.get();
| - borrow of `y` occurs here
16 | let b = y.x;
| ^ move out of `y.x` occurs here
However it lets the first main compile without issue:
fn main() {
let mut x = 3;
let y = Foo { x: &mut x };
let z = y.get();
println!("{}", z);
}
Prints 3.
fshould beF, and you can cally.get()directly... and I really like this question!