I am trying to implement a system that will use borrower validation / life to provide secure custom indexes in the collection. Consider the following code:
struct Graph(i32); struct Edge<'a>(&'a Graph, i32); impl Graph { pub fn get_edge(&self) -> Edge { Edge(&self, 0) } pub fn split(&mut self, Edge(_, edge_id): Edge) { self.0 = self.0 + edge_id; } pub fn join(&mut self, Edge(_, edge0_id): Edge, Edge(_, edge1_id): Edge) { self.0 = self.0 + edge0_id + edge1_id; } } fn main() { let mut graph = Graph(0); let edge = graph.get_edge(); graph.split(edge) }
Links to the graph borrowed by the Edge structure should be discarded when calling methods such as split or join . This will correspond to the API invariant that all edge indices must be destroyed when the chart is mutated. However, the compiler does not understand this. It does not work with messages like
error[E0502]: cannot borrow `graph` as mutable because it is also borrowed as immutable --> src/main.rs:23:5 | 22 | let edge = graph.get_edge(); | ----- immutable borrow occurs here 23 | graph.split(edge) | ^^^^^ mutable borrow occurs here 24 | } | - immutable borrow ends here
If I understand this correctly, the compiler does not understand that borrowing a graph that occurred in the edge structure is actually freed up when the function is called. Is there a way to teach the compiler what I'm trying to do here?
Bonus question: is there a way to do the same, but without borrowing the graph in the Edge structure? The edge structure is used only as temporary for traversal purposes and will never be part of the external state of the object (I have "weak" versions of this edge).
Addendum : after some digging, it seems like this is really far from trivial. First of all, Edge(_, edge_id) doesn’t actually destroy Edge because _ doesn’t bind at all (yes, i32 is Copy, which makes things even more complicated, but it’s easy to fix, structure without copying). Secondly, even if I completely destroy Edge (i.e. by doing this in a separate area), the link to the chart still exists, although it should be moved (this should be a mistake). It only works if I perform a destructuring in a separate function. Now I have an idea how to get around it (having a separate object that describes the state change and destroys the indexes as they are delivered), but it very quickly becomes very inconvenient.