Kyle L.
Kyle L.

Reputation: 335

Cannot assign to struct field because it is already borrowed, yet borrowed field is in separate scope

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

Answers (2)

Kyle L.
Kyle L.

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

Chayim Friedman
Chayim Friedman

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

Related Questions