Reputation: 335
I am running into borrow checker issue. I have a mutable variable. I call a function on it that takes &self
. Later I try to modify a field of that mutable variable, but the borrow checker protests: "cannot assign to w.field1
because it is borrowed".
The error message help docs suggest moving the call to the function to a separate scope, however that didn't work.
I included a simplified test scenario that includes some of the other structural elements that may be at play.
struct BaseShape<'s> {
parent: Option<*mut Group<'s>>,
}
struct Circle<'s> {
shape: BaseShape<'s>,
radius: f64,
}
struct Group<'s> {
shape: BaseShape<'s>,
children: Vec<ShapeWrapper<'s>>,
}
enum ShapeWrapper<'s> {
Circle(Circle<'s>),
}
struct DataObject<'r, 's> {
data: &'r ShapeWrapper<'s>,
computation: bool,
}
impl<'r, 's: 'r> ShapeWrapper<'s> {
fn inner_compute(&'s self) -> DataObject<'r, 's> {
DataObject {
data: &self,
computation: true,
}
}
}
struct World<'s> {
items: Vec<ShapeWrapper<'s>>,
field1: bool,
}
impl<'r, 's: 'r> World<'s> {
fn compute(&'s self) -> DataObject<'r, 's> {
let item = self.items.first().unwrap();
item.inner_compute()
}
}
pub fn run_main() {
let item = ShapeWrapper::Circle(Circle {
shape: BaseShape {
parent: Option::None,
},
radius: 1.0,
});
let mut w = World {
items: vec![item],
field1: false,
};
// ****** separate scope
{
let r = w.compute();
assert_eq!(r.computation, true);
}
// ****** why is referenced element still held on here?
w.field1 = true;
}
The error I am getting is:
error[E0506]: cannot assign to `w.field1` because it is borrowed
--> src/lifetimes_multiple.rs:60:5
|
57 | let r = w.compute();
| ----------- borrow of `w.field1` occurs here
...
60 | w.field1 = true;
| ^^^^^^^^^^^^^^^
| |
| assignment to borrowed `w.field1` occurs here
| borrow later used here
Upvotes: 3
Views: 501
Reputation: 335
Thanks to the helpful suggestions from Francis Gagné, Chayim Friedman and John Kugelman, the issue was that I was being a little too explicit on the lifetimes. The solution is to only preserve the lifetime signature of the long-lived 's, and keep any other lifetime references anonymous/implicit, thus allowing the compiler to infer the appropriate lifetimes for the shorter-lived references. Here is a portion of the changed code:
struct DataObject<'r, 's> {
data: &'r ShapeWrapper<'s>,
computation: bool,
}
impl<'s> ShapeWrapper<'s> {
fn inner_compute(&self) -> DataObject<'_, 's> {
DataObject {
data: self,
computation: true,
}
}
}
struct World<'s> {
items: Vec<ShapeWrapper<'s>>,
field1: bool,
}
impl<'s> World<'s> {
fn compute(&self) -> DataObject<'_, 's> {
let item = self.items.first().unwrap();
item.inner_compute()
}
}
Upvotes: 1
Reputation: 71605
When you take &'s self
in compute()
, you tell the compiler "I may keep self
borrowed as long as 's
". 's
is defined in World
, and deduced to be the whole lifetime of w
. So, you borrow w
for the rest of its life. But then you're trying to use it (borrow it mutably)! You have a mutable and a shared reference to the same object at the same type (as far as the borrow checker is concerned).
See What is the difference between '&self' and '&'a self'?.
Upvotes: 0