0

In the following rust example, a Values structure holds a list of values and a Refs structure holds some references to those values. This code yields a compiler error, shown at the bottom of the post, indicating that in generate_ref self.values essentially must have a lifetime of 'a, making it impossible to generate ref2 even though the reference used to generate ref1 is in its own code block.

pub struct Values {
    values: Vec<i32>,
}

impl<'a> Values {
    pub fn new() -> Values {
        Values { values: vec![] }
    }

    pub fn generate_ref(&mut self) -> &'a mut i32 {
        self.values.push(1);
        self.values.last_mut().unwrap()
    }
}

pub struct Refs<'a> {
    ref1: &'a mut i32,
    ref2: &'a mut i32,
}

impl<'a> Refs<'a> {
    pub fn new(values: &'a mut Values) -> Refs {
        let ref1 = { values.generate_ref() };
        let ref2 = { values.generate_ref() };
        Refs { ref1, ref2 }
    }
}

fn main() {
    let mut values = Values::new();
    let refs = Refs::new(&mut values);
    let ref3 = { values.generate_ref() };
}
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src\main.rs:12:9
   |
12 |         self.values.last_mut().unwrap()
   |         ^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 10:5...
  --> src\main.rs:10:5
   |
10 | /     pub fn generate_ref(&mut self) -> &'a mut i32 {
11 | |         self.values.push(1);
12 | |         self.values.last_mut().unwrap()
13 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src\main.rs:12:9
   |
12 |         self.values.last_mut().unwrap()
   |         ^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 5:6...
  --> src\main.rs:5:6
   |
5  | impl<'a> Values {
   |      ^^
note: ...so that reference does not outlive borrowed content
  --> src\main.rs:12:9
   |
12 |         self.values.last_mut().unwrap()
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

What I really need is to ensure that the reference returned from generate_ref lasts as long as the value stored in Values. How can I do that? If that is not possible, is there a different way to structure this code that is valid in Rust?

Edit

For more context, this is a simplified example. In the actual implementation, Values holds onto a bus::Bus to broadcast data to the receivers. The receivers are produced by the Bus. Various other structs contain receivers (bus::BusReader) and references to the broadcaster (&mut bus::Bus), but only one broadcaster exists for each channel.

mentoc3000
  • 214
  • 2
  • 14
  • 1
    Can you explain why you need 2 mutable i32 references in a struct and how you intend to use them? – pretzelhammer Dec 18 '20 at 20:49
  • 2
    You can't modify a vector while holding a reference to one of its elements. Trying to do so is unsound, since appending a new element to the vector may reallocate the vector, invalidating all existing references. – Sven Marnach Dec 18 '20 at 21:00
  • 1
    As @pretzelhammer said, what's the purpose, as that changes the approach. You could implement it similarly to how you'd implement graph structures, i.e. either use an `Rc` or return an `Id`. See "[Implement graph-like datastructure in Rust](https://stackoverflow.com/questions/34747464/implement-graph-like-datastructure-in-rust)" – vallentin Dec 18 '20 at 21:01
  • @pretzelhammer This is a simplified example. The real code has references to two more complex structures. Additionally, the `Vec` is as HashMap, though I suspect it will encounter the same issue that @Sven Marnach mentioned. – mentoc3000 Dec 18 '20 at 21:30
  • 1
    @mentoc3000 If that is the case then I think you over-simplified your example to the point where we can't meaningfully help you. Please update your question to show code that is more realistic to your actual use-case. – pretzelhammer Dec 18 '20 at 22:37

1 Answers1

0

While it's possible to return multiple mutable references into a Vec using methods like split_at_mut it's not very scalable and rarely worth the trouble. The recommended solution is to refer to elements by index instead of by reference, this removes all the lifetimes from your code and makes it significantly simpler and easier to manage:

pub struct Values {
    values: Vec<i32>,
}

impl Values {
    pub fn new() -> Values {
        Values { values: vec![] }
    }

    pub fn generate_index(&mut self) -> usize {
        self.values.push(1);
        self.values.len() - 1
    }
}

pub struct Indices {
    idx1: usize,
    idx2: usize,
}

impl Indices {
    pub fn new(values: &mut Values) -> Self {
        Indices {
            idx1: values.generate_index(),
            idx2: values.generate_index(),
        }
    }
}

fn main() {
    let mut values = Values::new();
    let indicies = Indices::new(&mut values);
    let idx3 = values.generate_index();
}

A downside of the above approach is you can't ever remove elements from the Vec as that would shift all of the indices, however that problem can be solved by using a crate like generational_arena which is like a Vec but it returns indices that are valid and persist even when elements are removed.

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
  • Another problem is that `Indices` can't actually access the values, since it does not hold a reference to the `Values` instance. And you lose the static checking that references provide – your indices will become invalid if you are not careful, and the compiler won't be able to help you with this kind of bug. – Sven Marnach Dec 18 '20 at 21:27
  • Yes, `Values` would have to be passed around with `Indices`. And "losing the static checking that references provide" is a feature and not a bug of this implementation, as OP's original approach with mut refs would never be allowed by the compiler. Indices becoming invalid is no more or less safe than using `std::rc::Weak`, so I don't see the issue there. – pretzelhammer Dec 18 '20 at 22:28