Moving a field of an object in vector index - rust

My question aims at understanding a deeper variant of the "cannot move out of index" error, specifically when I want to move a field of a struct that resides in a vector. Below is a classical example of a vector of objects and an attempt to move a field out of it.
struct Foo {
str_val: String,
int_val: i32,
}
fn main() {
let mut foos = Vec::new();
foos.push(Foo {
str_val: "ten".to_string(),
int_val: 10,
});
foos.push(Foo {
str_val: "twenty".to_string(),
int_val: 20,
});
// Here understandable error.
let moved = foos[0];
// Why also an error? "cannot move out of index of `Vec<Foo>`"
let moved_field = foos[0].str_val;
}
My question:
while I do understand why one cannot move the whole object occupying the element of the index (i.e. because the contiguous index is its owner), but
what I don't understand is why one cannot move a field of such an object.
The logical counter-reasoning I have is that it is allowed to move a field out of a standalone object, and rust appears advanced enough to track the ownership of fields separately:
let mut standalone = Foo {
str_val: "thirty".to_string(),
int_val: 30,
};
let moved_thirty = standalone.str_val;
// Rust seems to be completely ok with tracking fields separately.
let int_moved = standalone.i32;
// "use of moved value: `standalone.str_val`"
let error = standalone.str_val;

When one make a partial move out of a variable the parent variable cannot be used as a whole anymore, since you have stored the object in a vector this is forbidden, for instance the vector may need to move to reallocate more space.

Related

Rust error :Cannot return value referencing temporary value

I'm trying to make a code that returns the mode of a list of given numbers.
Here's the code :
use std::collections::HashMap;
fn mode (vector: &Vec<i32>) -> Vec<&&i32> {
let mut occurrences = HashMap::new();
let mut n= Vec::new();
let mut mode = Vec::new();
for i in vector {
let j= occurrences.entry(i).or_insert(0);
*j+=1;
}
for (num, occ) in occurrences.clone().iter() {
if occ> n[0] {
n.clear();
mode.clear();
n.push(occ);
mode.push(num);
} else if occ== n[0] {
mode.push(num);
}
}
mode
}
fn main () {
let mut numbers: Vec<i32>= vec![1,5,2,2,5,3]; // 2 and 5 are the mode
numbers.sort();
println!("the mode is {:?}:", mode(&numbers));
}
I used a vector for the mode since a dataset could be multimodal.
Anyway, I'm getting the following error:
error[E0515]: cannot return value referencing temporary value
--> src/main.rs:26:5
|
13 | for (num, occ) in occurrences.clone().iter() {
| ------------------- temporary value created here
...
26 | mode
| ^^^^ returns a value referencing data owned by the current function
When you return from the current function, any owned values are destroyed (other than the ones being returned from the function), and any data referencing that destroyed data therefore cannot be returned, e.g.:
fn example() -> &str {
let s = String::from("hello"); // owned data
&s // error: returns a value referencing data owned by the current function
// you can imagine this is added by the compiler
drop(s);
}
The issue you have comes from iter(). iter() returns an iterator of shared references:
let values: Vec<i32> = vec![1, 2, 3];
for i in values.iter() {
// i is a &i32
}
for i in values {
// i is an i32
}
So when you call occurrences.clone().iter() you're creating a temporary value (via clone()) which is owned by the current function, then iterating over that data via shared reference. When you destructure the tuple in (num, occ), these are also shared references.
Because you later call mode.push(num), Rust realizes that mode has the type Vec<&i32>. However, there is an implicit lifetime here. The lifetime of num is essentially the lifetime of the current function (let's call that 'a), so the full type of mode is Vec<&'a i32>.
Because of that, you can't return it from the current function.
To fix
Removing iter() should work, since then you will be iterating over owned values. You might also find that you can remove .clone() too, I haven't looked too closely but it seems like it's redundant.
A couple of other points while you're here:
It's rare to interact with &Vec<Foo>, instead it's much more usual to use slices: &[Foo]. They're more general, and in almost all cases more performant (you can still pass your data in like: &numbers)
Check out clippy, it has a bunch of linter rules that can catch a bunch of errors much earlier, and usually does a good job explaining them: https://github.com/rust-lang/rust-clippy

Insert in arbitrary nested HashMap

I want to have a data-structure that allows me to have arbitrary nested HashMap. For that I've constructed the following struct:
struct Database {
children: HashMap<String, Database>,
data: String,
}
For inserting in this structure i get a list of keys and a value to insert. So for example for the input
let subkeys = vec!["key1", "key1.1", "key1.1.3"];
let value = "myvalue";
I want the database to have this (pseudo) structure:
{
"data" : "",
"children": {
"key1": {
"data" : "",
"children": {
"key1.1": {
"data" : "",
"children" : {
"key1.1.3": {
"data": "myvalue",
"children" : {}
}
}
}
}
}
}
}
and then for example for a second insert request
let subkeys = vec!["key1", "key1.1", "key1.1.2"];
let value = "myvalue2";
the structure should look (pseudo) like this:
{
"data" : "",
"children": {
"key1": {
"data" : "",
"children": {
"key1.1": {
"data" : "",
"children" : {
"key1.1.3": {
"data": "myvalue",
"children" : {}
},
"key1.1.2": {
"data": "myvalue2",
"children" : {}
}
}
}
}
}
}
}
So here is a minimal reproducible example of what I've tried (not working)
playground
use std::collections::HashMap;
struct Database {
children: HashMap<String, Database>,
data: String,
}
fn main()
{
// make a databse object
let mut db = Database {
children: HashMap::new(),
data: "root".to_string(),
};
// some example subkeys
let subkeys = vec!["key1", "key1.1", "key1.1.3"];
// and the value i want to insert
let value = "myvalue";
// a reference to the current HashMap
// initialize with the root
let mut root = &db.children;
// iterate throught subkeys
for subkey in subkeys.iter() {
// match the result of a get request to the hashmap
match root.get::<String>(&subkey.to_string()) {
Some(child) => {
// if the key already exists set the root to the children of the child
root = &child.children;
}
None => {
// if key doesnt exist add it with a ne empty hashmap
let d = Database{children: HashMap::new(), data: "".to_string()};
// set root to this new databse obejct
root = &d.children;
root.insert(subkey.to_string(), d);
}
}
}
}
So as I understand it there are to problems with this code:
&d.children get s dropped after the match and so root "kind of" has no value
also the root.insert seems to be a problem because root is a & reference, so the data it refers to cannot be borrowed as mutable`
What do I need to do to make my code work and produce results like shown above. Do I maybe need to change something in my struct Database?
First, some comments on what you have so far and why it doesn't work. root needs to be a mutable reference. Note the distinction between a mutable variable (let mut root = &db.children;) and a mutable reference (let root = &mut db.children;). The former allows the variable itself to be changed. The latter allows the data behind the reference to be changed. In this instance, we need both (let mut root = &mut db.children) because we not only change root as we iterate through the nodes, but we also modify the data behind the reference whenever we need to insert a new node.
The same thing applies to d in the inner loop (it needs to be a mutable variable), though as we'll see, mutating d isn't really what we want.
// if key doesnt exist add it with a ne empty hashmap
let d = Database{children: HashMap::new(), data: "".to_string()};
// set root to this new databse obejct
root = &mut d.children;
root.insert(subkey.to_string(), d);
Ignoring the errors for a moment, what should this code do? d is a new Database with no real data in it. Then, we set root to be the (empty) set of children of this new Database. Finally, we insert the new Database into root. But since we changed root in the second step, it's no longer the parent: we're inserting d as a child of itself!
We instead want to switch the order of the second two steps. But if we simply switch those two lines, we get the error
error[E0382]: borrow of moved value: `d`
--> src/main.rs:41:24
|
36 | let mut d = Database{children: HashMap::new(), data: "".to_string()};
| ----- move occurs because `d` has type `Database`, which does not implement the `Copy` trait
37 |
38 | root.insert(subkey.to_string(), d);
| - value moved here
...
41 | root = &mut d.children;
| ^^^^^^^^^^^^^^^ value borrowed here after move
So the problem is that d is no longer a local variable when we try to set root to its children. We need root to be the children of the just-inserted value. The usual idiom for this kind of thing is the entry API. It allows us to attempt to get a value from a HashMap and if it's not found, insert something. Most relevantly, this insertion returns a mutable reference to whatever value now resides at that key.
Now that section looks like
// if key doesnt exist add it with a new empty hashmap
let d = Database{children: HashMap::new(), data: "".to_string()};
// insert the new database object and
// set root to the hashmap of children
root = &mut root.entry(subkey.to_string()).or_insert(d).children;
At this point, we have an apparently working program. By adding a #[derive(Debug)] to Database, we can see what the database looks like with println!("{:#?}, db);. However, if we try to add in the second value, everything blows up. Rather than placing the two values side-by-side, they end up in completely separate branches of the database. This traces back to the commented out lines in the Some(child) branch of the match statement.
We'd like to set root to a mutable reference to child.children, but even just uncommenting that line without any changes causes the error that root is mutably borrow while borrowed elsewhere. The problem is that we're using the borrow in root.get(&subkey.to_string()) now. Before, since we ignored child and the other branch didn't use any data from that borrow, the borrow could end right away. Now it has to last for the whole duration of the match. This prevents us from borrowing mutably even in the None case.
Fortunately, since we're using the entry API, we don't need this match statement at all! The whole thing can just be replaced with
let d = Database {
children: HashMap::new(),
data: "".to_string(),
};
// insert the new database object and
// set root to the hashmap of children
root = &mut root.entry(subkey.to_string()).or_insert(d).children;
If the subkey already exists in the set of children, root.entry(...).or_insert(...) will point to that already existing child.
Now we just need to clean up the code. Since you're using it more that once, I'd recommend factoring the act of inserting a path of keys into a function. Rather than following the HashMap<String, Database> through the path, I'd recommend following the Database itself, since that will allow you to modify its data field at the end. To that end, I'd suggest a function with this signature:
impl Database {
fn insert_path(&mut self, path: &[&str]) -> &mut Database {
todo!()
}
}
Next, since we only need to create a new Database (d) when one doesn't already exist, we can use Entry's or_insert_with method to create the new database only when necessary. This is easiest when there's a function to create the new database, so let's add #[derive(Default)] to the list of derives on Database. That makes our function
impl Database {
fn insert_path(&mut self, path: &[&str]) -> &mut Self {
let mut root = self;
// iterate throught path
for subkey in path.iter() {
// insert the new database object if necessary and
// set root to the hashmap of children
root = root
.children
.entry(subkey.to_string())
// insert (if necessary) using the Database::default method
.or_insert_with(Database::default);
}
root
}
}
At this point we should run cargo clippy to see if there are any suggestions. There's one about using to_string on &&str. To fix that, you have two choices. One, use one of the other methods for converting &strs to Strings instead of to_string. Two, dereference the &&str before using to_string. This second option is simpler. Since we're iterating over &[&str] (Vec<&str>::iter in your original), the items in the iteration are &&str. The idiomatic way to strip off the extra layer of references is to use a pattern to destructure the items.
for &subkey in path {
^^^ this is new
... // subkey has type &str instead of &&str here
}
My last piece of advice would be to change the name of root to something more generic, like node. It's only the root right at the start, so the name is misleading after that. Here's the final code together with your tests (playground):
use std::collections::HashMap;
#[derive(Default, Debug)]
struct Database {
children: HashMap<String, Database>,
data: String,
}
impl Database {
fn insert_path(&mut self, path: &[&str]) -> &mut Self {
// node is a mutable reference to the current database
let mut node = self;
// iterate through the path
for &subkey in path.iter() {
// insert the new database object if necessary and
// set node to (a mutable reference to) the child node
node = node
.children
.entry(subkey.to_string())
.or_insert_with(Database::default);
}
node
}
}
fn main() {
// make a databse object
let mut db = Database {
children: HashMap::new(),
data: "root".to_string(),
};
// some example subkeys
let subkeys = vec!["key1", "key1.1", "key1.1.3"];
// and the value i want to insert
let value = "myvalue";
let node = db.insert_path(&subkeys);
node.data = value.to_string();
println!("{:#?}", db);
let subkeys = vec!["key1", "key1.1", "key1.1.2"];
let value = "myvalue2";
let node = db.insert_path(&subkeys);
node.data = value.to_string();
println!("{:#?}", db);
}

How do I insert into or update an RBTree from rbtree-rs?

I'm having trouble understanding how to idiomatically find and append to or create a new vector if the value is part of a data structure, in this case a Red Black tree.
I'm using this Red Black Tree implementation and the plan is to grab a mutable reference to the value which I will append to if it exists (not None) and create a new vector and move it to the RBTree if there is no value for the key. My code looks like this, slightly altered for brevity so excuse any careless errors:
struct Obj {
tree: RBTree<i32, Vec<String>>,
}
let mut obj = Obj {
tree: RBTree::new(),
};
let k = 5;
let v = "whatever";
match obj.tree.get_mut(k) {
None => {
let mut vec: Vec<Node> = Vec::new();
vec.push(v);
book.tree.insert(k, vec);
}
Some(vec) => vec.push(v),
}
The problem is that I'm getting a mutable reference to the tree when I check for existence because if it does exist, I want to mutate it by appending to the vector. However, if it does not exist, I want to insert a new node which tries to do a mutable borrow so I get a "second mutable borrow occurs here" on this line book.tree.insert(k, vec);.
I would love some insight into how to perform this find or create so that it compiles and is thread safe. I guess it's also possible the library I'm using has problems. Not yet qualified to comment on that.
In such cases the workaround is to move the mutating code outside of get_mut()'s scope.
let needs_insert = match obj.tree.get_mut(k) {
None => true,
Some(vec) => {
vec.push(v);
false
}
};
if needs_insert {
book.tree.insert(k, vec![v]);
}

Do Rust collections have the ability to own the data they store?

Given the following code (which does not compile):
fn main() {
let mut v = vec!();
{
let name = "Bob the Builder".to_string();
v.push(&name);
}
for m in &v{
println!("{}", m);
}
}
I have created a variable binding to a Rust String type which will go out of scope within the first set of curly braces. Is there a way to somehow move the ownership of the String such that the vector itself owns it?
This is an arbitrary example however I'm just trying to understand if this concept is possible.
I already know that if I use a string literal this will be regarded as a static string which will exist for the lifetime of the entire app and therefore this code would compile but I'm just trying to understand if a collection in Rust can own data. I know Rust is not Objective-C but Objective-C has collections with the ability to retain their data.
The vector will own it.. as long as you don't pass a reference to it.
Changing your code to this:
fn main() {
let mut v = vec!();
{
let name = "Bob the Builder".to_string();
v.push(name); // <--- no ampersand
println!("{}", name); // <---- error, use of moved value
}
for m in &v {
println!("{}", m);
}
}
..throws an error because name is now owned by the Vector. If you allow for the fact that the Vector now owns the string.. your code compiles (by removing my println! call):
fn main() {
let mut v = vec!();
{
let name = "Bob the Builder".to_string();
v.push(name); // <--- no ampersand
}
for m in &v {
println!("{}", m); // <--- works fine
}
}
So your problem is that you're passing a reference to your string into the vector. Essentially, at the end of the block your name value will be dropped and your &name reference in the Vector could potentially point to invalid memory.. making v[0].something_here() potentially dangerous. So the compiler stops you. But, if you transfer ownership of the name variable into the vector (by not passing a reference.. but passing the whole thing) then Rust knows to clean the string up when it cleans the Vector up.

Cannot move out of &mut pointer

I've implemented a simple linked list as a struct thusly
struct List {
data : String,
cons : Option<Box<List>>
}
I have another struct which has a member of this type, defined below
pub struct Context {
head : Option<Box<List>>
}
In a function of this struct, run, I have this code
let mut temp_head = &mut self.head;
let mut full_msg = "".to_string();
while temp_head.is_some() {
let temp_node = temp_head.unwrap();
full_msg.push_str(temp_node.data.as_slice());
temp_head = temp_node.cons;
}
To iterate through the linked list and assemble a string of their data. However, the line which sets the value of temp_node produces the following error: cannot move out of dereference of &mut-pointer, and the compiler also complains that the value I'm trying to put into temp_head at the end doesn't live past the block.
I've tried cloning temp_head on the first line or temp_node.cons on the last line to get versions with the lifespan that I want, but that just produces additional errors, and the real problem seems to be that I just don't understand why the first version doesn't work. Can someone explain what I'm doing wrong, and/or link me to the Rust docs which explain this?
You need to be very careful with references in your code, the problem is that first you indeed attempt to move the content of temp_head out of it's container, when using unwrap(). This content being moved would be destroyed at the end of the while block, leaving temp_head referring to deleted content.
You need to use references all the way, and for this pattern-matching is more appropriate than using unwrap() and is_some(), like this :
let mut temp_head = &self.head;
let mut full_msg = "".to_string();
while match temp_head {
&Some(ref temp_node) => { // get a reference to the content of node
full_msg.push_str(temp_node.data.as_slice()); // copy string content
temp_head = &temp_node.cons; // update reference
true // continue looping
},
&None => false // we reached the end, stop looping
} { /* body of while, nothing to do */ }

Resources