I'm fighting with the borrow checker. I have two similar pieces of code, one working as I expect, and the other not.
The one that works as I expect:
mod case1 {
struct Foo {}
struct Bar1 {
x: Foo,
}
impl Bar1 {
fn f<'a>(&'a mut self) -> &'a Foo {
&self.x
}
}
// only for example
fn f1() {
let mut bar = Bar1 { x: Foo {} };
let y = bar.f(); // (1) 'bar' is borrowed by 'y'
let z = bar.f(); // error (as expected) : cannot borrow `bar` as mutable more
// than once at a time [E0499]
}
fn f2() {
let mut bar = Bar1 { x: Foo {} };
bar.f(); // (2) 'bar' is not borrowed after the call
let z = bar.f(); // ok (as expected)
}
}
The one that doesn't:
mod case2 {
struct Foo {}
struct Bar2<'b> {
x: &'b Foo,
}
impl<'b> Bar2<'b> {
fn f(&'b mut self) -> &'b Foo {
self.x
}
}
fn f4() {
let foo = Foo {};
let mut bar2 = Bar2 { x: &foo };
bar2.f(); // (3) 'bar2' is borrowed as mutable, but who borrowed it?
let z = bar2.f(); // error: cannot borrow `bar2` as mutable more than once at a time [E0499]
}
}
I hoped I could call Bar2::f
twice without irritating the compiler, as in case 1.
The question is in the comment (3): who borrowed bar2
, whereas there is no affectation?
Here's what I understand:
In case 1, f2
call: the lifetime parameter 'a
is the one of the receiving &Foo
value, so this lifetime is empty when there is no affectation, and bar
is not borrowed after the Bar1::f
call;
In case 2, bar2
borrows foo
(as immutable), so the lifetime parameter 'b
in Bar2
struct is the foo
reference lifetime, which ends at the end of f4
body. Calling Bar2::f
borrows bar2
for that lifetime, namely to the end of f4
.
But the question is still: who borrowed bar2
? Could it be Bar2::f
? How Bar2::f
would hold the borrowed ownership after the call? What am I missing here?
I'm using Rust 1.14.0-nightly (86affcdf6 2016-09-28) on x86_64-pc-windows-msvc.
What is Borrowing? When a function transfers its control over a variable/value to another function temporarily, for a while, it is called borrowing. This is achieved by passing a reference to the variable (& var_name) rather than passing the variable/value itself to the function.
Borrowing is accessing the value of a variable without taking ownership of the variable by referencing the owner. The borrow checker ensures that the reference is valid, and the data isn't dropped using a construct called lifetimes. A lifetime is how long a variable exists.
Mutable References First, we change s to be mut . Then we create a mutable reference with &mut s where we call the change function, and update the function signature to accept a mutable reference with some_string: &mut String . This makes it very clear that the change function will mutate the value it borrows.
A reference represents a borrow of some owned value. You can get one by using the & or &mut operators on a value, or by using a ref or ref mut pattern.
Ah... you basically self-borrowed yourself.
The issue hinges on the fact that you have the same lifetime ('b
) used for both the lifetime of Foo
and the lifetime of Bar
. The compiler then dutifully unifies those lifetimes, and you end up in a strange situation where suddenly the lifetime of the borrow which should have ended at the end of the statement instead ends after the value should have gone out of scope.
As a rule of thumb: always use a fresh lifetime for self
. Anything else is weird.
It's interesting to note that this pattern can actually be useful (though more likely with an immutable borrow): it allows anchoring a value to a stack frame, preventing any move after the call to the function, which is (sometimes) useful to represent a borrow that is not well-modeled by Rust (like passing a pointer to the value to FFI).
In case #2, you have this:
impl<'b> Bar2<'b> {
fn f(&'b mut self) -> &'b Foo {
self.x
}
}
To highlight: &'b mut self
and &'b Foo
have the same lifetime specified.
This is saying that the reference to self
and the returned reference to an instance of a Foo
both have the same lifetime. Looking at the call site, you have this:
let foo = Foo {};
let mut bar2 = Bar2 { x: &foo };
So the compiler is inferring that both foo
and bar2
have the same lifetime. The lifetime of foo
is the scope of the f4
function, and so the mutable reference to bar2
shares this.
One way to fix this, is to remove the explicit lifetime on the self
reference:
fn f(&mut self) -> &'b Foo
This compiles and the compiler correctly understands that the reference to bar2
and the reference to foo
have different lifetimes.
Playground: https://play.rust-lang.org/?gist=caf262dd628cf14cc2884a3af842276a&version=stable&backtrace=0
TLDR: Yes, having the same lifetime specifier on the self reference and the returned reference means that the entire scope of f4
holds a mutable borrow of bar2
.
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