Hi.
I was trying to rebuild some old functions in rust (in particular: error correction using a trie). The algorithm requires keeping track of a set of states: node in the trie, position in the input, and some info on the error. In order to implement it efficiently, I want to allow these states to be able to refer to each other, like this:
struct State<'a> {
index: u32,
position: u16,
distance: u8,
previous: Option<&'a State<'a>>
}
These States would all live until the end of the function, and the previous
pointers form a sort of DAG that allows reconstruction later on. There are other ways to implement it, but they require copying and adding information from the previous to the new State, which I'd like to avoid.
In order to satisfy the lifetime requirements, I thought I could have a Vec (or LinkedList) which contains all States during the function's execution, like this:
let mut states: LinkedList<Box<State>> = LinkedList::new();
I hoped to use states
to keep track of all States, and be able to use &State in the other data structures (a queue of unprocessed States, and some sort of set to avoid duplicates). That however requires code to create a new State object that looks like this:
states.push_back(Box::new(State::new(...)));
let new_state = states.back().unwrap()
to which the compiler responds "cannot borrow states
as mutable because it is also borrowed as immutable", and when replacing back()
with back_mut()
that it cannot borrow states
as mutable twice.
I think I understand the reason why: allowing this could result in an invalid reference for new_state
when that particular element is removed from states
. I also think that that explains why e.g. list_tail
from LinkedList
is a Rawlink<Node<T>>
: the compiler doesn't know that the element list_tail
points at is kept alive by the fact that link_head
points at it indirectly.
So, my question: is that analysis correct, and that does mean I can only do what I planned to do using "unsafe" code, or using Rc<>
?
And the suggestion: if that is correct, it could be possible to have a type that guarantees its elements stay alive until it is destroyed itself, e.g. a Vec or LinkedList from which elements cannot be removed (and perhaps not mutated after being added either), and allow some relaxation of the borrowing rules for that type, since it is now possible to infer the lifetime of the element? That might help implementing certain algorithms (like mine) in a more efficient manner.