Lifetime conflict inside method, cannot resolve in any way other than locking the structure

Here is the code that I am experimenting with. Basically, what I want is, as it is common in OOP, have a struct that has a method that does certain modifications to the internal state of the struct. Here is the code. I left out irrelevant details and pasted the parts that are reported as erroneous by the compiler.

use std::collections::BTreeSet;

struct Structure <'a> {
    refs : Vec<Vec<&'a i32>>,
    tree : BTreeSet<i32>,
}

impl<'a> Default for Structure<'a> {
    fn default() -> Self {
        Self {
            refs : Default::default(),
            tree : Default::default()
        }
    }
}
impl<'a> Structure<'a> {
    fn foo (& mut self) {
        // ...
        if !self.tree.is_empty() {
            self.refs.push(Vec::default());
        }

        for _i in self.tree.iter() {
            self.refs.last_mut().unwrap().push(&_i);
        }
    }
}

fn main() {
    let mut structure : Structure = Default::default();
    structure.foo();
    structure.foo();
}

On compiling the code, the following error is output.

error[E0495]: cannot infer an appropriate lifetime for autoref due to confl
icting requirements                                                        
  --> main.rs:23:29                                                        
   |                                                                       
23 |         for _i in self.tree.iter() {                                  
   |                             ^^^^                                      
   |                                                                       
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined 
on the method body at 17:5...                                              
  --> main.rs:17:5                                                         
   |                                                                       
17 | /     fn foo (& mut self) {                                           
18 | |                                                                     
19 | |         if !self.tree.is_empty() {                                  
20 | |             self.refs.push(Vec::default());                         
...  |                                                                     
25 | |         }                                                           
26 | |     }                                                               
   | |_____^                                                               
note: ...so that reference does not outlive borrowed content               
  --> main.rs:23:19                                                        
   |                                                                       
23 |         for _i in self.tree.iter() {                                  
   |
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 16:6...
  --> main.rs:16:6
   |
16 | impl<'a> Structure<'a> {
   |      ^^
note: ...so that reference does not outlive borrowed content
  --> main.rs:24:48
   |
24 |             self.refs.last_mut().unwrap().push(&_i);
   |                                                ^^^

Well, my primary question. Can someone explain what happens here in terms of lifetime/borrowship? What causes the error? What parts of the compiler output might give a hint on how to fix it?

All I know so far is that if I modify the method signature like this,

    fn foo (&'a mut self) {                                                

, I get this

error[E0499]: cannot borrow `structure` as mutable more than once at a time
  --> main.rs:32:5
   |
31 |     structure.foo();
   |     --------- first mutable borrow occurs here
32 |     structure.foo();
   |     ^^^^^^^^^
   |     |
   |     second mutable borrow occurs here
   |     first borrow later used here

So the compiler thinks that I am trying to borrow via unique (mutable) reference, and that the borrow lives till the end of scope of that structure, right? But what I want to do is to call the method in the same scope that the structure belongs to and to be able to do it multiple times.

When you put <'a> on a struct, you are telling the compiler that the struct contains references to a storage location outside the struct, and that the compiler should use that lifetime to keep track of this external location to ensure the reference stays valid.

Since the reference you are actually storing points into the same struct, and not to an external location, you get lifetime errors, as what you told the compiler does not match what is actually happening.

It is not possible to write a struct where one field contains references to another using ordinary Rust references.

Why not just copy the integer instead of a reference?

3 Likes

I would do that, the code here is simplified, I actually want to store references to vectors that contain custom types. The data that these references point to is also stored in the structure.

Roughly here is the signature.

pub struct SearchStructure <'a> {
    data : Vec<CustomType>,
    // ref should point to `data` elements
    refs : Vec<Vec<&'a CustomType>>,
    tree : BTreeSet<CustomType>,
}

You can't use ordinary references for that. Consider using Rc to share it instead.

Here is your example with Rc:

use std::collections::BTreeSet;
use std::rc::Rc;

struct Structure {
    refs: Vec<Vec<Rc<i32>>>,
    tree: BTreeSet<Rc<i32>>,
}

impl Default for Structure {
    fn default() -> Self {
        Self {
            refs: Default::default(),
            tree: Default::default(),
        }
    }
}
impl Structure {
    fn foo(&mut self) {
        if !self.tree.is_empty() {
            self.refs.push(Vec::default());
        }

        for i in self.tree.iter() {
            self.refs.last_mut().unwrap().push(Rc::clone(i));
        }
    }
}

fn main() {
    let mut structure: Structure = Default::default();
    structure.foo();
    structure.foo();
}

Thanks! I am slightly confused about the fact that regular references cannot be used. Probably missed chapter 15 this one out. Works as expected now.

In general, borrowing from elements in the same struct just doesn't work in Rust's borrow checker rules. If you're interested in more reasons / details, the term to look for is "self referential data structures."

Despite the name, Rust's references are not a general-purpose way to refer to objects. They are temporary borrows, which lock objects in place and prevent them from being moved or mutated from elsewhere. When you take a reference, you're telling the compiler "I want that other thing to be frozen and unmoveable". That doesn't work when a struct tries to do this to itself.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.