How to create a struct with other field's reference

i created the next struct.
struct :

pub struct SourcePathManage<'a> {
    vec_ref_directory : Vec<& 'a PathBuf>, 
    directories : BTreeSet<PathBuf>
}
impl<'a> SourcePathManage<'a> {
    pub fn new() -> SourcePathManage<'a> {
        Self{
            vec_ref_directory : Vec::new(),
            directories : BTreeSet::new()
        }
    }

// test 0

    pub fn add_directory_0(& 'a mut self, path_string: String) {
        let path = PathBuf::from(path_string);
		self.directories.insert(path.parent().unwrap().to_path_buf().clone());
        self.add_vec_ref_0();
    }
    pub fn add_vec_ref_0(& 'a  mut self) {
        let directory = self.directories.last().unwrap();
        self.vec_ref_directory.push(directory);
    }
}

main code

  let mut bbbb = SourcePathManage::new();
    bbbb.add_directory_0("user/ex/m.rs".to_string());
    bbbb.add_directory_0("user/exx/m.rs".to_string());

-> here is an error
error :
cannot borrow bbbb as mutable more than once at a time
Is there anything I'm completely missing? Any way to work around that?

Here's a recent reply from a similar topic: How do I use a struct field with a lifetime<'a> correctly? - #2 by CAD97

1 Like

This is called a self referential struct and it's generally not possible to make with safe Rust, and the cases that are (including your own) become instantly unusable.

Rust assumes that any value is possible to move to a different location. That means that a borrow of a part of it would need to lock it in place while the borrow is live. That means that you borrow the struct, via add_directory_0, for as long as your internal reference is live. In this case, the duration of the borrow becomes "forever" to be able to solve the lifetime equation.

Your options are usually

  • Move the ownership of the items out of the struct. You could keep references in the BTreeMap as well.
  • Use something like Rc to have shared ownership of the items. This is closer to how a garbage collected language works.
  • Use one of the crates available to achieve self references anyway. These come with some caveats and may not always be applicable.
2 Likes

As others have described, storing references like this doesn't work well in Rust. So it makes sense to ask the question: Why do you need the vec_ref_directory?

If you are using it to allow iterating over all directories, note that you can do that using BTreeSet::iter(). This returns them in sorted order.

If you need stable integer indexes for accessing the directories, there are crates that could help with this. If you need this, I can help find one that works.

Rule of thumb is don't create types with lifetime parameters in normal application code; they're for library magic more than anything.

The quick, inaccurate version of the problem here, is that they must always live a shorter life than some other value, and you can't live a shorter life than yourself.

The fix is generally:

  • Use indices instead of keeping direct references into a collection, or
  • Slap some Rc in there too share the data ownership (but now you probably need something like RefCell to mutate things), or
  • Represent the state using function local variables instead of explicit data members, or
  • Use a library that handled doing the weird hard stuff for you
3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.