Cannot move out of &mut pointer - rust

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 */ }

Related

Moving a field of an object in vector index

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.

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

Is there a way to modify how I'm using variables in a loop so that the order I'm initializing them does not matter?

I'm fairly new to Rust, so not entirely sure how to properly title the question because I don't fully understand the error. I have the following simplified code which I'm using to parse command line arguments:
use std::env;
fn main() {
let mut script: &str = "";
// Get the commandline arguments.
let args: Vec<String> = env::args().collect();
// Loop through and handle the commandline arguments.
// Skip the first argument; it's the name of the program.
for arg in args.iter().skip(1) {
let split: Vec<&str> = arg.trim().split("=").collect();
if split.len() == 2 {
match split[0]{
"file" => { script = split[1]; }
_ => { println!("Invalid parameter: {}", arg); }
}
} else {
println!("Invalid parameter: {}", arg);
println!("Parameters should consist of a parameter name and value separated by '='");
}
}
}
Which gives me the following error:
error: `args` does not live long enough
--> src/main.rs:25:1
|
12 | for arg in args.iter().skip(1) {
| ---- borrow occurs here
...
25 | }
| ^ `args` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
When I change where the script variable is initialized:
use std::env;
fn main() {
// Get the commandline arguments.
let args: Vec<String> = env::args().collect();
let mut script: &str = "";
// Loop through and handle the commandline arguments.
// Skip the first argument; it's the name of the program.
for arg in args.iter().skip(1) {
let split: Vec<&str> = arg.trim().split("=").collect();
if split.len() == 2 {
match split[0]{
"file" => { script = split[1]; }
_ => { println!("Invalid parameter: {}", arg); }
}
} else {
println!("Invalid parameter: {}", arg);
println!("Parameters should consist of a parameter name and value separated by '='");
}
}
}
The error goes away. Based on the error and how the order that the variables are being initialized changes things, I think I'm making a fundamental mistake in how I'm using (borrowing?) the variables in the loop, but I'm not entirely sure what I'm doing wrong and the proper way to fix it. Is there a way to modify how I'm using the variables in the loop so that the order I'm initializing them does not matter?
Short answer: script refers to a portion of one of the strings allocated by env::args(). If you define script before args, then args is dropped first (as the compiler's message notes, "values are dropped in opposite order") and script points to deallocated memory. Your fix, defining the script after the args object, is correct.
To answer the edited question: the order of variables does matter when one of them is a reference to the other, and you are not allowed to change them arbitrarily. For an explanation of why that is so, read on.
In Rust, every reference is associated with a lifetime, the scope during which the reference is valid. To take an example from the book, lifetimes are what prevents the following from compiling (and crashing):
let r;
{
let x = 5;
r = &x;
}
println!("r: {}", r); // doesn't compile - x doesn't live long enough
In many cases, lifetimes are inferred automatically. For example, the following are equivalent:
{
let x = "foo";
let y: &str = "foo";
let z: &'static str = "foo";
}
i.e. compiler will infer the static lifetime given the use of a string constant, which is statically allocated and exists during the entire execution of the program. On the other hand, the following uses a narrower lifetime:
// correct
let s = "foo".to_owned(); // allocate "foo" dynamically
let sref = s.as_str(); // points to heap-allocated "foo"
...
Here, sref is only valid for as long as s is valid. After dropping or mutating s, sref would point to uninitialized memory, which Rust carefully prevents. Inserting extra braces sometimes helps visualize the scopes:
// correct - sref cannot outlive s
let s = "foo".to_owned();
{
let sref = s.as_str();
...
}
On the other hand, if you write them backwards, it doesn't compile:
// incorrect, doesn't compile
let mut sref = "";
let s = "foo".to_string();
sref = s.as_str();
To see why, let's insert more explicit scopes:
// incorrect, doesn't compile
{
let mut sref = "";
{
let s = "foo".to_string();
sref = s.as_str();
}
// <-- here sref outlives s
}
This is essentially the same as the example from the book, and it is obviously not allowed to compile! And now it should be a bit clearer what the compiler means by "values in a scope are dropped in the opposite order they are created". The fact that s is declared after sref means that it is effectively nested in an inner scope, which is why it will be dropped before the stuff in the outer scopes. sref referring to anything in s means that after the inner scope, sref is pointing to uninitialized memory.
To get back to your code, env::args() returns an Args object whose Iterator implementation yields dynamically allocated Strings. Although you start off by assigning a static &str to script, the lifetime of the script reference is determined as an intersection of the scopes of all assigned values. In this case these are the static scope from the first assignment and the scope of args from the second assignment, and their intersection is the args scope, which ends up being used as the reference lifetime. Moving script declaration after args places the script reference into an inner scope compared, ensuring that it always refers to a live object.
Is there a way to modify how I'm using variables in a loop so that the order I'm initializing them does not matter?
Yes, you can avoid borrowing at all by cloning the value:
use std::env;
fn main() {
let mut script = None;
for arg in env::args().skip(1) {
let mut parts = arg.trim().splitn(2, "=").fuse();
match (parts.next(), parts.next()) {
(Some("file"), Some(name)) => script = Some(name.to_owned()),
(Some(other), Some(_)) => {
println!("Invalid parameter: {}", other);
}
(Some(other), None) => {
println!("Invalid parameter: {}", other);
println!("Parameters should consist of a parameter name and value separated by '='");
}
(None, _) => {}
}
}
let script = script.expect("file is a required parameter");
do_thing_with_script(&script);
}
fn do_thing_with_script(_script: &str) {}
This also avoids allocating multiple Vecs. There's also a theoretical/potential memory savings as we don't have to keep the entire argument string in memory, just the parameter. On the flip side, there's a bit more allocation.
Profiling is always the right path, but it has yet to be my experience that command line processing is a large resource usage of a program. To that end, I advocate doing whichever route makes your code easiest to understand, maintain, and which gives your end users the best experience.
Usually that means using a library.
If you have your heart set on borrowing, then user4815162342's answer explains why you have to have the thing you are borrowing from outlive the borrow.

Replacing a borrowed Arc<RwLock>

I have some code that stores an object. I have a function that does the legwork of storing that object and returning an Arc of it.
struct Something {
// ...
}
// create a something, returning a locked Arc of it.
fn make_something(&mut self) -> Arc<RwLock<Something>>
{
let x = Something{};
let stored = Arc::new(RwLock::new(x));
// stored is cloned and put into a container in self
stored.clone()
}
Elsewhere, I have code that sometimes needs to get a new make_something, letting the old Something get stored elsewhere in make_something's Self. However, it gives me scoping problems:
fn elsewhere() {
let mut something_arc = obj.make_something();
let mut something_locked = something_arc.write().unwrap();
loop {
// something_lock is mutated as a Something
// create a new make something, and start a "transaction"
something_arc = obj.make_something();
something_locked = something_arc.write().unwrap();
}
}
The borrow checker is telling me that I can't replace something_arc because it's being borrowed by something_locked.
How do I replace something_arc and something_locked with a new Arc and associated write lock?

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.

Resources