Implement tree data structure - rust

I want to implement a tree data structure. I have a Node struct and want it to hold references to child Nodes. I tried:
use std::collections::*;
#[derive(Debug)]
struct Node {
value: String,
children: HashMap<String, Node>,
}
impl Node {
fn new(value: String) -> Self {
Node {
value: value,
children: HashMap::new(),
}
}
fn add_child(&mut self, key: String, value: String) -> &mut Node {
let mut node = Node::new(value);
self.children.insert(key, node);
&mut node
}
}
fn main() {
let mut root_node = Node::new("root".to_string());
root_node.add_child("child_1_1".to_string(), "child_1_1_value".to_string());
}
This code does not compile:
error: `node` does not live long enough
--> src/main.rs:22:10
|
22 | &mut node
| ^^^^ does not live long enough
23 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 19:67...
--> src/main.rs:19:68
|
19 | fn add_child(&mut self, key: String, value: String) -> &mut Node {
| ____________________________________________________________________^ starting here...
20 | | let mut node = Node::new(value);
21 | | self.children.insert(key, node);
22 | | &mut node
23 | | }
| |___^ ...ending here
error[E0382]: use of moved value: `node`
--> src/main.rs:22:10
|
21 | self.children.insert(key, node);
| ---- value moved here
22 | &mut node
| ^^^^ value used here after move
|
= note: move occurs because `node` has type `Node`, which does not implement the `Copy` trait
How can I implement this?

In this case, it's actually necessary to look at the second error message in the compiler output:
error[E0382]: use of moved value: `node`
--> src/main.rs:22:10
|
21 | self.children.insert(key, node);
| ---- value moved here
22 | &mut node
| ^^^^ value used here after move
|
= note: move occurs because `node` has type `Node`, which does not implement the `Copy` trait
The variable node is moved into the hashmap in line 21. You can't use it afterwards! In Rust we have move semantics, meaning that everything gets moved by default instead of cloned by default (C++) or referenced by default (Java). You want to return a reference to the Node object inside the hashmap!
An easy way would be to insert node as you are already doing and afterwards fetching the value from the hashmap:
let mut node = Node::new(value);
self.children.insert(key.clone(), node);
self.children.get_mut(key).unwrap()
This should make clear what the function actually does. However, this code has some disadvantages: First, we have to clone key (we need it for the insertion and the query) and secondly, the hashmap needs calculate the hash of the key twice which is not very efficient.
Luckily, Rust's HashMap has a nice entry()-API. We could change the function like that:
self.children.entry(key).or_insert_with(|| Node::new(value))
This is the whole body of add_child()! Now, however, we notice that ... we haven't really thought about what is supposed to happen if the hashmap already contains a value associated with the given key! In the code above, the old value is kept and returned. If you want to do something else (e.g. replace the value), you could just use match on the Entry object:
let node = Node::new(value);
match self.children.entry(key) {
Entry::Occupied(e) => {
// Maybe you want to panic!() here... but we will just
// replace the value:
e.insert(node); // discarding old value...
e.get_mut()
}
Entry::Vacant(e) => insert(node),
}

Related

Rust cloning HashMap<String, Object> without moving into closure

I am trying to make my own programming language in rust, and most features are done, so I thought I could add to the UnknownIdentifier error the ability to find the closest match to whatever the user wanted
However before I even got to finding the closest match I found out that cloning HashMap<String, Object> moves it into the closure
ErrorGenerator::error function:
#[allow(non_snake_case)]
mod ErrorGenerator {
pub fn error(name: &str, explanation: &str, line: usize, col: usize, file: String, after_f: Box<dyn Fn() -> ()>) -> ! {
eprintln!("\n[ERROR] {}, Line {:?}, Column {:?}", file, line, col);
eprintln!(" {}: {}", name, explanation);
after_f();
exit(1);
}
}
ErrorGenerator::error(
"UnknownIdentifier",
&format!("unknown identifier: `{}`, this identifier could not be found", tokenc.repr()),
tokenc.line,
tokenc.col,
tokenc.file,
Box::new(||{
let mut hashc: Vec<String> = hashs.clone().into_keys().collect();
hashc.sort();
}),
);
This is the error it gives:
error[E0597]: `hashs` does not live long enough
--> src/runtime/runtime.rs:960:70
|
959 | Box::new(||{
| - -- value captured here
| _____________________________________|
| |
960 | | let mut hashc: Vec<String> = hashs.clone().into_keys().collect();
| | ^^^^^ borrowed value does not live long enough
961 | | hashc.sort();
962 | | }),
| |______________________________________- cast requires that `hashs` is borrowed for `'static`
...
1203 | }
| - `hashs` dropped here while still borrowed
The problem's solution is probably either:
A way to borrow in 'static lifetime a mutable variable created in a method into a closure
or
A way to clone HashMap<String, Object> without moving it into the closure
You can find the full code in https://github.com/kaiserthe13th/tr-lang/tree/unknown-id-err-impl
What happens is that the compiler doesn't clone hashs then passes the clone to your callback; instead, it passes a reference to hashs to your callback and clones it inside the callback.
However, the callback is required to be 'static, and if it holds a reference to the containing function it is not! So the compiler is complaining.
What you want is to clone the hashmap before, then pass the clone to the callback. Like:
ErrorGenerator::error(
"UnknownIdentifier",
&format!("unknown identifier: `{}`, this identifier could not be found", tokenc.repr()),
tokenc.line,
tokenc.col,
tokenc.file,
{
let hashc = hashs.clone();
Box::new(|| {
let mut hashc: Vec<String> = hashc.into_keys().collect();
hashc.sort();
})
},
);
If you'll do that, you'll also recognize that the closure needs to be FnOnce() since you're moving out of hashc (.into_keys()). So after_f: Box<dyn FnOnce()>.

How to make Rust temporary value live longer?

I'm still learning Rust and have the following code.
use std::collections::BTreeMap;
#[derive(Debug)]
struct MyStruct {
a: String,
b: String,
}
fn main() {
let mut hash = BTreeMap::new();
let data = vec![
MyStruct {
a: "entry1".to_string(),
b: "entry1 body".to_string(),
},
MyStruct {
a: "entry2".to_string(),
b: "entry2 body".to_string(),
}
];
let re = regex::Regex::new(r#".(\d)"#).unwrap();
for item in &data {
for m in re.captures_iter(&item.b) {
hash.insert(&m[1].parse::<i32>().unwrap(), &item.a);
}
}
println!("{:#?}", hash);
}
It generates an error:
error[E0716]: temporary value dropped while borrowed
--> src\main.rs:26:26
|
26 | hash.insert(&m[1].parse::<i32>().unwrap(), &item.a);
| ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| | |
| | creates a temporary which is freed while still in use
| borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
What's the correct way to fix it? I tried to put &m[1].parse::<i32>().unwrap() in a variable but of no avail.
The BTreeMap structure should either be the owner of the data and key inserted, or the data and key should have a 'static lifetime (same as HashMap and other collections). In this case, the key being used is i32, which has the Copy trait defined for it, so simply removing the & reference should pass the i32 value in as the key. For the data, you will either want to clone the string rather than & borrow, but you could also rewrite the loop to consume the data vector and pass the item.b string value in without the need to clone.

In Rust, I have a large number of receiver objects I'd like manage, however I'm running into lifetime issues using Select

Due to the possibility of there being a large number of objects, I'd like to have a way to add them to the select list, remove them for processing and then add them back. All, without having to rebuild the select list each time an object is added back for waiting. It looks something like this:
use std::collections::HashMap;
use crossbeam::{Select, Sender, Receiver};
struct WaitList <'a> {
sel: Select<'a>,
objects: HashMap<u128, Object>,
sel_index: HashMap<usize, u128>,
}
impl<'a> WaitList<'a> {
fn new () -> Self { Self { sel: Select::new(), objects: HashMap::new(), sel_index: HashMap::new() } }
fn select(&self) -> &Object {
let oper = self.sel.select();
let index = oper.index();
let id = self.sel_index.get(&index).unwrap();
let obj = self.objects.get(&id).unwrap();
obj.cmd = oper.recv(&obj.receiver).unwrap();
self.sel.remove(index);
obj
}
fn add_object(&self, object: Object) {
let id = object.id;
self.objects.insert(id, object);
self.add_select(id);
}
fn add_select(&self, id: u128) {
let idx = self.sel.recv(&self.objects.get(&id).unwrap().receiver);
self.sel_index.insert(idx, id);
}
}
Over time the select list will contain more dead entries, then live, and I'll rebuild it at that time. But, I'd like to not have to rebuild it every time. Here's the detailed error message:
Checking test-select v0.1.0 (/Users/bruce/Projects/rust/examples/test-select)
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/main.rs:28:47
|
28 | let idx = self.sel.recv(&self.objects.get(&id).unwrap().receiver);
| ^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 27:5...
--> src/main.rs:27:5
|
27 | / fn add_select(&self, id: u128) {
28 | | let idx = self.sel.recv(&self.objects.get(&id).unwrap().receiver);
29 | | self.sel_index.insert(idx, id);
30 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:28:34
|
28 | let idx = self.sel.recv(&self.objects.get(&id).unwrap().receiver);
| ^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 9:6...
--> src/main.rs:9:6
|
9 | impl<'a> WaitList<'a> {
| ^^
note: ...so that the types are compatible
--> src/main.rs:28:28
|
28 | let idx = self.sel.recv(&self.objects.get(&id).unwrap().receiver);
| ^^^^
= note: expected `&mut crossbeam::Select<'_>`
found `&mut crossbeam::Select<'a>`
While I believe I understand the issue, that the borrow of the receiver from the hash table doesn't live long enough, I'm having a difficult time trying to come up with an alternative -- and I'm not seeing a clean way to borrow the information. I considered creating a struct to contain the borrow, and using that instead of a plain id in the wait sel_index however that runs into the same lifetime problem.
struct SingleWaiter<'a> {
id: u128,
receiver: &'a Receiver::<Command>
}
I feel like I'm missing something or not understanding something, as it seems like it shouldn't be that difficult to do what I want to do. I can imagine that the choice of HashMap for holding object might be the issue, a Vec felt wrong, as I'm adding and inserting. BTW, the HashMap isn't normally part of the waitlist. It is part of something else, but the problem remains the same irregardless of where the HashMap lives.

Value used after move?

My code:
fn add(&mut self, key: &str, path: std::path::PathBuf) {
self.key.push(key.to_string());
self.path.push(path);
if key == "img" {
self.img.push(self.create_img(path))
}
}
Error:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/software/init/image.rs:44:27
|
44 | self.img.push(self.create_img(path))
| -------- ---- ^^^^ second mutable borrow occurs here
| | |
| | first borrow later used by call
| first mutable borrow occurs here
error[E0382]: use of moved value: `path`
--> src/software/init/image.rs:44:43
|
40 | fn add(&mut self, key: &str, path: std::path::PathBuf) {
| ---- move occurs because `path` has type `std::path::PathBuf`, which does not implement the `Copy` trait
41 | self.key.push(key.to_string());
42 | self.path.push(path);
| ---- value moved here
43 | if key == "img" {
44 | self.img.push(self.create_img(path))
| ^^^^ value used here after move
It is fairly hard to reason about this code without other methods, but the following code should work (or at least be closer to working solution):
fn add(&mut self, key: &str, path: std::path::PathBuf) {
self.key.push(key.to_string());
// Here, path must be explicitly cloned because of move semantics.
self.path.push(path.clone());
if key == "img" {
// Sometimes compiler cannot determine dependencies of compound
// statement, so with `img` bounding `img` we un-borrow `self`
// (if `create_img` returns owned type). Otherwise you need some
// reference counter like std::rc::Rc.
let img = self.create_img(path);
self.img.push(img);
}
}

Rust lifetimes for struct references

I've just started with Rust but can't quite grasp lifetimes so I could resolve following issue by myself:
This test project is about simulating a bit to allow tracing it through various bitwise operations, e.g. let newbit = oldbit1 ^ oldbit2 and looking at newbit I can tell afterwards it came out of an XOR operation with oldbit1 and oldbit2 as operands.
#[derive(Copy,Clone)]
pub enum TraceOperation {
AND,
OR,
XOR,
NOT,
}
#[derive(Copy,Clone)]
pub struct TraceBit<'a> {
source_a: Option<&'a TraceBit<'a>>,
source_b: Option<&'a TraceBit<'a>>,
source_op: Option<TraceOperation>,
value: bool,
}
This compiles, but I don't fully understand why the lifetime parameters are needed that way. I assume that the compiler cannot expect that the members source_a and source_b live as long as the struct itself as this may not hold true, so explicit lifetimes are required.
is this assumption correct?
Further I don't fully understand why I have to re-specify the lifetime parameter for the reference type, i.e. why I have to write source_a: Option<&'a TraceBit<'a>> as opposed to source_a: Option<&'a TraceBit>.
What is the second lifetime used for? How do I read that line out loud? I have:
"source_a is a variable of type Option that may have Some reference (that is valid at least as long as the struct itself and as long as member source_b) to an instance of TraceBit"
My final issue is that I cannot make it to work using an overloaded operator:
use std::ops::BitXor;
impl<'a> BitXor for TraceBit<'a> {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self {
let valA: usize = if self.value { 1 } else { 0 };
let valB: usize = if rhs.value { 1 } else { 0 };
let val = if valA ^ valB != 0 { true } else { false };
TraceBit { source_a: Some(&self), source_b: Some(&rhs), source_op: Some(TraceOperation::XOR), value: val }
}
}
This is basically pure guessing based on BitXor documentation. So what I try to do, in a very explicit manner, is to perform an xor operation on the two input variables and create a new TraceBit as output with the inputs stored in it as reference.
error[E0597]: `self` does not live long enough
--> libbittrace/src/lib.rs:37:30
|
37 | TraceBit { source_a: Some(&self), source_b: Some(&rhs), source_op: Some(TraceOperation::XOR), value: val }
| ^^^^ does not live long enough
38 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 31:1...
--> libbittrace/src/lib.rs:31:1
|
31 | / impl<'a> BitXor for TraceBit<'a> {
32 | | type Output = Self;
33 | | fn bitxor(self, rhs: Self) -> Self {
34 | | let valA: usize = if self.value { 1 } else { 0 };
... |
40 | |
41 | | }
| |_^
error[E0597]: `rhs` does not live long enough
--> libbittrace/src/lib.rs:37:53
|
37 | TraceBit { source_a: Some(&self), source_b: Some(&rhs), source_op: Some(TraceOperation::XOR), value: val }
| ^^^ does not live long enough
38 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 31:1...
--> libbittrace/src/lib.rs:31:1
|
31 | / impl<'a> BitXor for TraceBit<'a> {
32 | | type Output = Self;
33 | | fn bitxor(self, rhs: Self) -> Self {
34 | | let valA: usize = if self.value { 1 } else { 0 };
... |
40 | |
41 | | }
| |_^
error: aborting due to 2 previous errors
Seems like nothing lives longer than the xor operation itself, but how can I resolve this?
I've tried various workarounds/changes to the code but to no avail and in any way I rather like to understand the issue than guessing a correct solution....
Tree-like structures must use the Box pointer type (Option<Box<TraceBit>>). In general, in structs you should prefer owned types.
Rust references aren't mere pointers. They are borrows (compile-time read/write locks) of data that must exist as owned somewhere else.
So if you have an owned version of TraceBit:
pub struct TraceBit {
source_a: Option<Box<TraceBit>>,
}
then reference to it is of type: &'a TraceBit, but references to a type don't change how the type looks internally, so the type of source_a is still Box<TraceBit>. You can keep getting the &'a TraceBit references recursively step by step:
trace_bit = trace_bit.source_a.as_ref().unwrap();
but there's no construct in Rust where taking a reference to the root of a tree suddenly changes the whole tree into a tree of references, so the type you are creating can't exist, and that's why you can't get type annotations right.
Maybe instead of passing references around, you should use a contained and cloneable name type.
use std::rc::Rc;
#[derive(Debug)]
pub enum TraceOperation {
AND,
OR,
XOR,
NOT,
}
#[derive(Debug)]
pub enum BitName<T> {
Name(Rc<T>),
Combination(Rc<(TraceOperation, BitName<T>, BitName<T>)>),
}
impl<T> Clone for BitName<T> {
fn clone(&self) -> Self {
match self {
&BitName::Name(ref x) => BitName::Name(Rc::clone(x)),
&BitName::Combination(ref x) => BitName::Combination(Rc::clone(x)),
}
}
}
impl<T> From<T> for BitName<T> {
fn from(x:T) -> Self {
BitName::Name(Rc::new(x))
}
}
impl<T> BitName<T> {
pub fn combine(op : TraceOperation, a : &Self, b :&Self) -> Self {
BitName::Combination(Rc::new((op, (*a).clone(), (*b).clone())))
}
}
fn main() {
let x : BitName<String> = BitName::from(String::from("x"));
let y : BitName<String> = BitName::from(String::from("y"));
let xandy = BitName::combine(TraceOperation::AND, &x, &y);
println!("{:?}", xandy);
}

Resources