1

Im trying to make a grid of Elements so every Element can have references to its neighbors. My Element looks like

struct Element<'a> {
    value: u32,
    neighbors: Vec<&'a Element<'a>>,
}

This Elements are stored in a 2d Vec like

struct Vec2D<'a> {
    vec_2d: Vec<Element<'a>>,
    col: usize,
}

During neighbor calculation, Im trying to store each neighbors reference to its neighbor.

fn calculate_neighbors(&mut self) {
        let col = self.col as isize;

        let mut indices = Vec::new();
        let i = (self.vec_2d.len() - 1) as isize;

        if i % col == 0 {
            // left edge
            //Im doing a bunch of inserts here. Refer Rust playgroud link
        } else if (i + 1) % col == 0 {
            //right edge
            //Im doing a bunch of inserts here. Refer Rust playgroud link
        } else {
            //middle
            //Im doing a bunch of inserts here. Refer Rust playgroud link
        }

        let valid_neighbors_indices: Vec<usize> = indices.into_iter().map(|e| e as usize).filter(|e| *e < self.vec_2d.len() && *e >= 0).collect();
        println!("{} => {:?}", i, valid_neighbors_indices);

        let last_element = self.vec_2d.last_mut().unwrap(); //last must be there.

        valid_neighbors_indices.into_iter().for_each(|e| {
            let neighbor = self.vec_2d.get_mut(e).unwrap(); //all indices in valid_neighbors_indices are valid. so unwrap() is fine.
            // Following two lines are problematic:

            last_element.neighbors.push(neighbor);
            neighbor.neighbors.push(last_element);
        });
    }

I get a big error and even after spending a lot of time I cant resolve it.

Can someone explain the error and fix?

Here is the Rust playground link https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c45e02f1e5bc5ca8b66aa77a41323d44

Boss Man
  • 587
  • 2
  • 12
  • 3
    This is [Why can't I store a value and a reference to that value in the same struct?](https://stackoverflow.com/q/32300132) just with a different lens – kmdreko Oct 27 '20 at 00:01
  • @kmdreko I read the link but I cant connect the dots I guess. The lifetime of all elements in `Vec` is the same ie `'a`. So why cant one element refer to another element? – Boss Man Oct 27 '20 at 19:36
  • Also I cant make any sense of the error. – Boss Man Oct 27 '20 at 19:40
  • You're making a self-referential data structure. Rust doesn't allow you to because there are too many footguns (with mutating, drop-ordering, moving) and therefore there's no way to express the lifetimes properly. `&'a SomeStruct<'a>` is almost never what you want and is indicative of self-references. In your `add` you're mutating the vec that the references are supposed to be bound to, potentially invalidating them. Even if you're fixing them immediately in `calculate_neighbors` it doesn't matter, invalidating references at any point breaks Rust's invariants and is undefined behavior. – kmdreko Oct 27 '20 at 20:28
  • In the dupe there are links to crates that provide workarounds but I'm not sure how to apply them in this scenario. Regardless, since you're using vectors, a workaround would be to use indices instead of references. Perhaps less convenient, but more safe. – kmdreko Oct 27 '20 at 20:30
  • @kmdreko I see your point and just so I understand: this `&'a SomeStruct<'a>` is not allowed or rather not possible because the reference and referring struct cannot have same lifetime? Because the referring struct might go out of scope while still having a reference? – Boss Man Oct 28 '20 at 00:14
  • 1
    The `&'a SomeStruct<'a>` syntax is *allowed* because the compiler can adjust lifetimes to make it work in some cases, but its usually incorrect (or at least imprecise) and can cause issues. See [this](https://stackoverflow.com/questions/32089410/lifetimes-and-references-to-objects-containing-references), [this](https://stackoverflow.com/questions/32165917/why-does-linking-lifetimes-matter-only-with-mutable-references), and [this](https://stackoverflow.com/questions/50946525/why-do-the-lifetimes-on-a-trait-object-passed-as-an-argument-require-higher-rank) – kmdreko Oct 28 '20 at 00:47
  • Im still looking for a concrete answer to my question rather than links. Although Im learning things about lifetimes just not about the issue im facing. – Boss Man Oct 28 '20 at 01:34

0 Answers0