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
self
taken byslice()
needs to outlive theslice()
function call.Therefore,
tree
remains mutably borrowed after thetree.slice()
call in themain
function.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
?