I'm playing around with ASTs in Rust, and one thing which I thought would be neat would be if all nodes could simply contain slices into the original source string, rather than copying memory around. However, I can't quite work out how I should structure the lifetimes in my code such that this goal is achieved. My naive attempt yielded this program:
#[derive(Debug)]
struct Node<'source> {
slice : &'source str,
nid : usize ,
parent : usize ,
children: Vec<usize> ,
}
#[derive(Debug)]
struct Tree<'source> {
source: String ,
nodes : Vec<Node<'source>>,
}
impl<'source> Tree<'source> {
fn new() -> Self {
Tree {source: String::from("Hello World"), nodes: vec![]}
}
fn slice(&'source mut self, from: usize, to: usize) {
let new_nid = self.nodes.len();
self.nodes.push(
Node { slice: &self.source[from..to], nid: new_nid, parent: 0, children: vec![] }
);
}
}
fn main() {
let mut tree = Tree::new();
tree.slice(2, 6);
println!("{:?}", tree);
}
Compiler Output:
error[E0502]: cannot borrow `tree` as immutable because it is also borrowed as mutable
--> ast.rs:32:22
|
31 | tree.slice(2, 6);
| ---- mutable borrow occurs here
32 | println!("{:?}", tree);
| ^^^^ immutable borrow occurs here
33 | }
| - mutable borrow ends here
This does not compile because:
The slices contained by the nodes need to outlive the
slice()function callTherefore, the mutable reference to
selftaken byslice()needs to outlive theslice()function call.Therefore,
treeremains mutably borrowed after thetree.slice()call in themainfunction.Therefore, the
println!macro cannot compile.
Ideally, the slices within the nodes need to be able to outlive the slice() call, but the mutable reference self should not. I understand that this is forbidden by the borrow checker, as self contains the source string, into which the slices reference.
With this in mind, how can I restructure my code to allow nodes to contain slices into source, without having to borrow self for longer than the length of the slice function? Is this feasible at all, or need I resort to unsafe?