Help with multiple mutable reference borrow because of lifetimes

Hey there!
I'm trying to do the following, have three structs, one which stores a vector of data, one which stores some metadata and a reference to the data struct, and a third one which manages these other two, such as:

pub struct GameModel {
    vertices: Vec<Vec4>,
    normals: Vec<Vec4>,
    indices: Vec<(i32, i32, i32)>,
}

pub struct GameObject<'a> {
    position: Vec3,
    rot_x: f32,
    rot_y: f32,
    rot_z: f32,
    size: f32,
    model: &'a GameModel,
}

pub struct Scene<'b> {
    models: HashMap<String,GameModel>,
    objects: Vec<GameObject<'b>>
}

And in Scene's implementation I'd like to have a create_object function such as:

impl<'c> Scene<'c> {
    .
    .
    .

    pub fn create_object(&'c mut self, pos: Vec3, rot_x: f32, rot_y: f32, rot_z: f32, scale: f32, model: &str) {
        if self.models.contains_key(model) {
            self.objects.push(
                GameObject::create(pos,rot_x,rot_y,rot_z,scale, self.models.get(model).unwrap())
            );
        }
    }
}

The problem I'm running into is if i use this function, afterwards any function use which has "&mut self" in it throws an "cannot borrow scene as mutable more than once at a time" error.

I'm guessing that this is probably a problem with my thinking, and this should probably be done in some other way in rust, but I'm not sure what that way is. Any help is appreciated.

You should not put temporary references in structs. They're unable to store data by definition, and because they're temporary views and bound to a scope, they make the whole struct only usable in a single scope and only temporarily. To store something "by reference", the correct type is Box, or just store the struct directly inline (by value):

pub struct GameObject {
    position: Vec3,
    rot_x: f32,
    rot_y: f32,
    rot_z: f32,
    size: f32,
    model: GameModel,
}

Also lifetimes on &mut self have a very limited use, and almost always are a huge mistake. In your case it &'c mut self says this method can be called once and only once, and after that the object is completely unsuable until the end of its life. This is because Scene<'c> requires 'c to outlive the Scene (there can be no dangling pointers, so anything in Scene needs to live at least as long), and &'c mut borrows for as long as 'c, so from perspective of self that's forever, and it's borrowing exclusively.

In short, don't put references in structs.

3 Likes

Okay, that clears a lot of things up :slight_smile:

Box is similar to a unique_ptr in c++, right? In this case if I wouldn't like to copy the vector data for each GameObject then I should use Rc<>, right?

(Wanted to avoid Rc<>, because in my mind GameModel should have clear ownership by Scene)

as an aside, assuming you don't have a reason not to use a transform, you probably want to use one in place of the position/rotation/scaling data. e.g euclid or nalgebra crates.

Yes, Box is a unique owner, and allocates on the heap. If the GameModel isn't large or moved around frequently, then storing it without any indirection is fine. It's still exclusively owned by GameObject.

Rc would be heap-allocated, shared, single-threaded, read-only. For shared objects you'll commonly see Arc<T> or Arc<Mutex<T>> since they're compatible with multi-threading.

3 Likes

(The object has a model() function which returns a 4x4 transformation model matrix :P)

1 Like

Okay, thanks, GameModel may be huge, so I'm going to look into Arc and Arc<Mutex>.

Many thanks!

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.