0

I'm trying to write a very simple draw batcher. I've created a minimal version of the issue:

struct DrawBatch<'a> {
    triangles : &'a mut i32,
}
struct Batch<'a> {
    draw_batch : Vec<DrawBatch<'a>>,
    default_count : i32
}

impl<'a> Batch<'a> {
    fn circle<'e: 'a>(&'e mut self) {
        // a circle is made up of multiple triangles (fan)
        self.triangle();
        self.triangle();
    }
    
    fn triangle<'m : 'a>(&'m mut self) {
        // draws a triangle
        let batch = self.get_last_batch();
        // modify the batch, add triangles, etc 
        *batch.triangles += 1;
        println!("triangle count: {}", batch.triangles);
    }
    
    fn get_last_batch<'r : 'a>(&'r mut self) -> &mut  DrawBatch {
        if self.draw_batch.is_empty() {
            // there must always be at least one draw batch
            self.draw_batch.push(DrawBatch { triangles: &mut self.default_count });
        }
        return self.draw_batch.last_mut().unwrap();
    }
}

fn main() {
    let de = 0;
    let mut b = Batch{ draw_batch: Vec::new(), default_count : de };
    b.circle();
}

You can play it here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a89d11f89680962554e486fd05f129ab

The error is:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:13:9
   |
9  | impl<'a> Batch<'a> {
   |      -- lifetime `'a` defined here
...
12 |         self.triangle();
   |         ---------------
   |         |
   |         first mutable borrow occurs here
   |         argument requires that `*self` is borrowed for `'a`
13 |         self.triangle();
   |         ^^^^^^^^^^^^^^^ second mutable borrow occurs here

I truly don't understand the problem. The first borrow is clearly finished after I borrow again. I've tried multiple ways of setting the lifetimes but I could not find the right configuration.

Why is triangles a reference??

triangles : &'a mut i32,

In reality this is something else (A material struct specifically) I just wanted to keep things light and don't introduce more structs.

The question this was close for has absolutely nothing to do with my question. Different errors. This question is not about storing a object + object reference in the same struct.

E_net4
  • 27,810
  • 13
  • 101
  • 139
frankelot
  • 13,666
  • 16
  • 54
  • 89
  • This is a feature of the language, to make sure there is only one owner of an object. Why do you need the liftetimes? – Simson Mar 16 '23 at 10:38
  • I don't. I was guided by the compiler to introduce them. Is there a way to make the above work without lifetimes? – frankelot Mar 16 '23 at 10:40
  • Adding lifetime annotations won't help you to violate aliasing rules. – Peter Hall Mar 16 '23 at 10:41
  • I don't want to violate the rules. On the contrary, I want to make the code work, and understand lifetimes in the process. I read multiple sources but I still don't get why the above won't compile – frankelot Mar 16 '23 at 10:42
  • The linked answer contains an explanation of why you cannot do what you are trying to do. If it doesn't then this one can be re-opened. – Peter Hall Mar 16 '23 at 10:50
  • 1
    I think that was the wrong duplicate. The real problem is that the `self` parameter in `fn triangle<'m : 'a>(&'m mut self)` has type `&'m mut Batch<'m>` which is always wrong (actually `&'m mut Batch<'a>` but since `'m: 'a` the same reasoning applies). See the [correct duplicate](https://stackoverflow.com/q/66252831/5397009). – Jmb Mar 16 '23 at 11:19
  • 1
    On second thought both duplicates apply. The immediate error message is caused by the `&'m mut Batch<'m>` issue, but the root cause is that you're trying to push a `DrawBatch` that borrows `self.default_count` into `self.draw_batch`, thus storing both `default_count` and a reference to `default_count` in the same `Batch`. – Jmb Mar 16 '23 at 11:27
  • 1
    #![deny(elided_lifetimes_in_paths)] helped me identify the issue! It was indeed the missing <'a> at the very end of `get_last_batch(&mut self) -> &mut DrawBatch`. It would be great if there were a question with this title and an answer like this. Since it seems to be a pretty common problem for beginners: https://github.com/rust-lang/rust/issues/91639#issuecomment-1254356212 – frankelot Mar 16 '23 at 12:57

0 Answers0