Are variables obtained by destructuring a tuple mutable? - rust

Does destructuring a tuple make the variable mutable?
This is the code snippet that confuses me:
let tup = ("duck", 11, true);
let (_, data, ..) = tup;
println!("data: {}", data);
let (_, .., data) = tup; // I expected that this is not available
println!("data: {}", data);
data = "what"; // like this, but then this one why not?

let actually defines a new variable. So in your code, two data variables exist, one created by the first let, and one created by the second let.
That said, the second let data shadows the name of the first one, making the first one exist in the background somewhere but being inaccessible because its name has been reused by the second one.
This is also the reason why you don't need to declare the first data as mut - you never mutate it. It still exists in the background, the new data variable just stole the name.
The last line fails because data now refers to the second data object, which is a boolean. And you cannot assign strings to booleans. Apart of that, it isn't declared mut, so even if you wrote data = false;, it would fail to compile.
You can replace the last line with let data = "what";, then it will compile. But then you have three data variables now, with the previous two being shadowed.
To demonstrate that it is actually shadowed, here is a little example. It temporarily shadows the variable inside of the nested scope; after the scope the previous variable is reachable again because the nested one got dropped:
fn main() {
let tup = ("duck", 11, true);
let (_, data, ..) = tup;
println!("data: {}", data);
{
let (_, .., data) = tup;
println!("data: {}", data);
}
println!("data: {}", data);
}
data: 11
data: true
data: 11

Related

Match and consume enum in Rust two times does not work

how can I match the value of an enum two times?
The problems seems to occur, if the value of an enum is "consumed" in a match.
I don't understand why I get the error message "use of moved value ... value used here after move" -- see code below
I would understand to get the error if I return just the value but I am returning a clone of value and still get the error.
// Here a simple enum
enum SomeEnum {
X(String),
}
// Then later in some function ...
// Test Enum which works
let x = SomeEnum::X(String::from(("a")));
let x_val1 = match x {
SomeEnum::X(_) => 1
};
println!("x_val1 = {}", x_val1);
let x_val2 = match x {
SomeEnum::X(_) => 1
};
println!("x_val2 = {}", x_val2);
// Test Enum which does not work
let y = SomeEnum::X(String::from(("b")));
let y_val1 = match y {
SomeEnum::X(value) => value.clone()
};
println!("y_val1 = {}", y_val1);
// Does not compile, here I get error message ...
// use of moved value
//
// value used here after move
let y_val2 = match y {
SomeEnum::X(value) => value.clone()
};
println!("y_val2 = {}", y_val2);
By default, match statements consume all they can, so the value will be moved and owned.
The compiler already suggests a workaround:
help: borrow this field in the pattern to avoid moving y.0
SomeEnum::X(ref value) => value.clone(),
See keyword ref:
Using the ref keyword, the value is only borrowed, not moved, making it available for use after the match statement:
What you currently do is letting the match take ownership and then clone. But at that point, ownership is already "gone". So the compiler complains about it.
But you can have it even simple in today’s Rust. If you match on a borrowed value match &y, then all bindings will attempt to borrow as well.
This was introduced with match ergonomics.

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

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]);
}

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.

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