Struct which own references of other owned structs

Hello,

I don't know how I can achieve that: I want a struct which have two properties:

  • A list of some structs
  • A HashMap containing references on these structs

The following example is a State owning a list of structs with 2d position. This State must own a HashMap containing list of struct refs indexed by the positions:

use std::collections::HashMap;

struct MyStruct {
    position: (i32, i32),
}

struct MainState<'a> {
    my_structs: Vec<MyStruct>,
    my_structs_by_positions: HashMap<(i32, i32), Vec<&'a MyStruct>>,
}

impl MainState<'_> {
    fn new() -> Self {
        let mut my_structs: Vec<MyStruct> = vec![];

        for x in 0..2 {
            for y in 0..2 {
                my_structs.push(
                    MyStruct {
                        position: (x, y),
                    }
                )
            }
        }

        let mut main_state = Self {
            my_structs,
            my_structs_by_positions: HashMap::new(),
        };

        for my_struct in &main_state.my_structs {
            if let Some(my_structs_refs) = main_state.my_structs_by_positions.get_mut(&my_struct.position) {
                my_structs_refs.push(my_struct);
            } else {
                main_state.my_structs_by_positions.insert(my_struct.position, vec![my_struct]);
            };
        }

        main_state
    }
}

fn main() {
    let main_state = MainState::new();
}

But, this produce the following errors:

error[E0515]: cannot return value referencing local data `main_state.my_structs`
  --> src/main.rs:39:9
   |
31 |         for my_struct in &main_state.my_structs {
   |                          ---------------------- `main_state.my_structs` is borrowed here
...
39 |         main_state
   |         ^^^^^^^^^^ returns a value referencing data owned by the current function


error[E0505]: cannot move out of `main_state` because it is borrowed
  --> src/main.rs:39:9
   |
13 |     fn new() -> Self {
   |                 ---- return type is MainState<'1>
...
31 |         for my_struct in &main_state.my_structs {
   |                          ---------------------- borrow of `main_state.my_structs` occurs here
...
39 |         main_state
   |         ^^^^^^^^^^
   |         |
   |         move out of `main_state` occurs here
   |         returning this value requires that `main_state.my_structs` is borrowed for `'1`

How can I setup these internal references in MainState ?

The problem is that my_structs may move its contents around in memory, which would invalidate the references. The easiest way to do this is store indices instead of references:

use std::collections::HashMap;

struct MyStruct {
    position: (i32, i32),
}

struct MainState {
    my_structs: Vec<MyStruct>,
    my_structs_by_positions: HashMap<(i32, i32), Vec<usize>>,
}

impl MainState {
    fn new() -> Self {
        let mut my_structs: Vec<MyStruct> = vec![];

        for x in 0..2 {
            for y in 0..2 {
                my_structs.push(
                    MyStruct {
                        position: (x, y),
                    }
                )
            }
        }

        let mut main_state = Self {
            my_structs,
            my_structs_by_positions: HashMap::new(),
        };

        for (i, my_struct) in main_state.my_structs.iter().enumerate() {
            main_state.my_structs_by_positions
                      .entry(my_struct.position)
                      .or_default()
                      .push(i);
        }

        main_state
    }
}

fn main() {
    let main_state = MainState::new();
}

Playground

Hello @2e71828
Thanks for your time ! I think this is a working solution for me :slight_smile:
Additional question: with your solution, if I change my_structs content, i have to recompute my_structs_by_positions no ?

In the general case, yes. If you add and remove elements without changing the position of existing ones, however, you can avoid having to do a full reindex. Using something like slab instead of Vec for my_structs could be helpful if you're going to be frequently adding and removing elements.

1 Like

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.