0

I am trying to make a program in which the overall state is stored in a struct, some elements of which hold references to other elements. A very simplified example follows:

use std::sync::Mutex;

struct Species{
    index : usize,
    population : Mutex<usize>
}
struct Grid<'a>{
    squares : Mutex<Vec<&'a Species>>
}
struct MyProgram<'a>{
    species : Vec<Species>,
    grid : Grid<'a>
}
impl<'a> MyProgram<'a>{
    pub fn new() -> Self{
        let dogs = Species{index : 0, population : Mutex::new(100)};
        let cats = Species{index : 1, population : Mutex::new(0)};
        let species = vec!{dogs, cats};
        let grid = Grid{squares : Mutex::new(vec![&dogs; 100])};
        MyProgram{species, grid}
    }
    pub fn place_animal(&self, position : usize, species : &'a Species){
        let mut grid = self.grid.squares.lock().unwrap();
        let mut new_population = species.population.lock().unwrap();
        *new_population+= 1;
        drop(new_population);
        let mut old_population = species.population.lock().unwrap();
        *old_population-= 1;
        drop(old_population);
        grid[position] = species;
    }
}

The (oversimplified) idea is that a large grid consists of squares that may be occupied by animals, which is represented by each square holding a reference to the species that lives there, with population updated as squares change hands.

This version does not compile. As is it complains about borrowing dogs after a move, but a few different permutations result in different errors. I don't want to go into the specifics because I understand there's a deeper problem here - even if I got this to compile, nothing would guarantee that the species vector could not be modified later, potentially dropping elements that grid still holds references to. Because of that, I assume there isn't really a way to make this work with the data types I'm using here.

I believe I could solve this using Arcs, but that feels like a cludge. In my use case, species are never added or removed after initialization; instead the entire vector will be dropped when MyProgram leaves scope, at which point all of the references will die as well. It feels like there should be some way to communicate that to the borrow checker.

I also want to avoid using Arcs due to the overhead, as grid may become quite large.

What I'm really looking for is some way to guarantee that some of the data within MyProgram will live as long as MyProgram itself, and therefor references may be used safely without additional reference counting or other checks.

Edward Peters
  • 2,433
  • 1
  • 12
  • 26
  • You are right that there is a deeper problem, but you are wrong thinkg that "if I got this to compile, nothing would guarantee that the `species` vector could not be modified later, ...". It's exactly to prevent *this* kind of error that Rust won't compile your program, that is, if you achieved to compile, you could be pretty sure (unless you have done something *really* dirty) that you will have no such issues. – BlackBeans Apr 20 '22 at 05:05

0 Answers0