I am trying to learn Rust, specifically lifetimes and smart pointers and would like to know the most Rusty way of doing this.
Specifically, assume we are designing a mock language and "analyzer":
a=1 # each line is of form name=number|name
b=2
c=a # a is "copied" to c and should have value 1
a=4 # a and all its references should now have value 4
Lets design some bogus "Memory Node" abstraction
Code | Memory Nodes |
---|---|
a=1 |
Node { name: "a", value: 4 } |
b=2 |
Node { name: "b", value: 2 } |
c=a |
Node { name: "c", value: 1 } |
a=4 |
Node { name: "a", value: 4 } |
Note: We want only three memory nodes to exist and not four like a AST
So that I can resolve values of variables, I must keep a context from name to "Memory Node"
To keep my analyzer's API clean I don't want to expose this Context.
Lets say our base structs look like these:
struct Root {
pub nodes: Vec<&Node>,
}
struct Node {
pub name: String,
pub value: u64,
}
struct Context {
nodes: HashMap<String, Node>,
}
impl Node {
fn new(line: &str, context: &mut Context) -> Node {
// Add self to context
// If node exists in context, update it and return the same
// return new node
}
}
However, we cant keep copies of Node in both Context and Root.
One way is to wrap node up like Rc<RefCell<Node>>
everywhere, this would make the client API not neat. I would ideally like to maintain Root
containing a Vec of &Node
as the public API. An example implementation is here https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7ec7ecd29eea2626a11b90c829777e30
When I try this using lifetimes I get an error that I cant really circumvent - https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5c5054872806b94a5b3a8ad4a75c1282
It gives:
error[E0499]: cannot borrow `context` as mutable more than once at a time
--> src/main.rs:49:39
|
43 | fn new(text: &str) -> Root {
| - let's call the lifetime of this reference `'1`
...
49 | let node = Node::new(row, &mut context);
| ^^^^^^^^^^^^ `context` was mutably borrowed here in the previous iteration of the loop
...
52 | Root { nodes, context }
| ----------------------- returning this value requires that `context` is borrowed for `'1`
error[E0505]: cannot move out of `context` because it is borrowed
--> src/main.rs:52:23
|
43 | fn new(text: &str) -> Root {
| - let's call the lifetime of this reference `'1`
...
49 | let node = Node::new(row, &mut context);
| ------------ borrow of `context` occurs here
...
52 | Root { nodes, context }
| --------------^^^^^^^--
| | |
| | move out of `context` occurs here
| returning this value requires that `context` is borrowed for `'1`
Some errors have detailed explanations: E0499, E0505.
For more information about an error, try `rustc --explain E0499`.
Alternatively what are other Rusty ways to do this, without using Smart pointers if possible?