3

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
Kyle L.
  • 315
  • 2
  • 6
  • I'm not very proficent at Rust but as far as I understand, you can only have one mutable reference of an object. You can think your reference as w which can be accessed in the entire scope. Maybe try with `let ref = &mut w; ref.compute();` in your sub scope. – Berkays Jan 13 '22 at 18:06
  • 1
    [`&'a self` is an antipattern](https://stackoverflow.com/questions/45833618/what-is-the-difference-between-self-and-a-self). – Chayim Friedman Jan 13 '22 at 18:54
  • 1
    [Here's a version that compiles.](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8e3d10cee972cdadcbe763789744e68f) Basically, you wrote `&'s self` but I think you meant `&'r self` (per `DataObject`'s definition). – Francis Gagné Jan 13 '22 at 19:13

2 Answers2

1

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()
    } 
}
Kyle L.
  • 315
  • 2
  • 6
0

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'?.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • Thank you for the suggestion. I had tried something similar before, but the error message made me think I was going down the wrong path. Here is a playground link that shows the resulting error: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7430bad1329cdf5980411d97194dab8f – Kyle L. Jan 14 '22 at 03:21