Match and consume enum in Rust two times does not work - rust

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.

Related

value dropped when trying to assign a slice

I have a very simple case where I have some function that takes a Option<Vec>, it then needs to look at that option, and if it is a None, then have a empty byte string, but if it is a Some, then call a function that does some transofmration of it's input.
Sketched out, it looks like this:
pub fn transform(ad: &[u8]) -> Vec<u8> {
ad.to_vec()
}
pub fn func(plaintext: Option<Vec<u8>>) {
let out = "".as_bytes();
if plaintext != None {
let out = transform(&plaintext.unwrap());
}
}
Doing the unwrapping and the if like this is really ugly though,and I would much like to do this in a safer way, maybe with pattern matching:
pub fn transform(ad: &[u8]) -> Vec<u8> {
ad.to_vec()
}
pub fn func(plaintext: Option<Vec<u8>>) {
let out = match plaintext {
Some(x) => &transform(&x),
None => "".as_bytes()
};
}
But this gives the error:
|
16 | let out = match plaintext {
| --- borrow later stored here
17 | Some(x) => &return_smth(&x),
| ^^^^^^^^^^^^^^-
| | |
| | temporary value is freed at the end of this statement
| creates a temporary which is freed while still in use
|
= note: consider using a `let` binding to create a longer lived value
I am unsure about which value that is being talked about here. How do I call my function, and get a slice returned?
I am unsure about which value that is being talked about here.
The one that's returned by transform (or return_smth, or whatever else you call it): that returns a Vec (which is an owned value), you immediately borrow it to a slice, but you never actually store the vec, so at the end of the expression it gets dropped and you have a dangling reference.
How do I call my function, and get a slice returned?
There are two main ways:
You don't, would be my first recommendation here. Rust has a container called Cow which stores "an owned value or a reference", which you can use like a normal reference, or convert to an owned value:
let out = match plaintext {
Some(x) => transform(&x).into(),
None => b"".into(),
};
The second possibility is to store the owned at the highest level you need it, then create a local borrow to that e.g.
let mut _v;
let out = match plaintext {
Some(x) => {
_v = transform(&x);
&*_v
},
None => b"",
};
This is a relatively common pattern for non-trivial code paths where you need to borrow part of the owned value rather than the entirety of it.
Incidentally,
as you can see above Rust has bytes literals (b""), no need to create a string then byteify it
as jmb commented, Rust has high-level methods for common generic tasks. Both "apply transformation to option's value" and "get value or return default" are such
Vec::new() is guaranteed not to allocate, so creating an empty Vec (or String) is not a big deal.
Hence if you don't have a very strong reason to favor a slice
let out = plaintext
.as_deref()
.map_or_else(Vec::new, transform);
would be perfectly fine here, or even
let out = plaintext.map_or_else(Vec::new, transform);
if you change transform to take and return a Vec.

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

Why can I directly match an array of Options but not a variable containing an array of Options?

The following code compiles:
fn consume(_: Box<u64>) {}
let tuple = (Some(Box::new(1)), Some(Box::new(2)));
match tuple {
(Some(x), Some(y)) => {
consume(x);
consume(y);
}
_ => (),
}
The following code compiles:
fn consume(_: Box<u64>) {}
match [Some(Box::new(1)), Some(Box::new(2))] {
[Some(x), Some(y)] => {
consume(x);
consume(y);
}
_ => (),
}
But this code does not compile:
fn consume(_: Box<u64>) {}
let array = [Some(Box::new(1)), Some(Box::new(2))];
match array {
[Some(x), Some(y)] => {
consume(x);
consume(y);
}
_ => (),
}
The compiler says:
error[E0382]: use of moved value: `(array[..] as std::prelude::v1::Some).0`
--> src/main.rs:5:24
|
5 | [Some(x), Some(y)] => {
| - ^ value used here after move
| |
| value moved here
|
= note: move occurs because the value has type `std::boxed::Box<u64>`, which does not implement the `Copy` trait
Why do the first and second version compile, but not the third version?
Here's a reduced version of your code:
struct NonCopy;
fn main() {
// OK
let tuple = (Some(NonCopy), Some(NonCopy));
if let (Some(_x), Some(_y)) = tuple {}
// OK
if let [Some(_x), Some(_y)] = [Some(NonCopy), Some(NonCopy)] {}
// Fails
let array = [Some(NonCopy), Some(NonCopy)];
if let [Some(_x), Some(_y)] = array {}
}
The good news
This code works as-is when non-lexical lifetimes are enabled.
The bad news
Non-lexical lifetimes aren't stable yet.
The workaround
Explicitly transfer ownership of the array to the match or if let head expression:
let array = [Some(NonCopy), Some(NonCopy)];
if let [Some(_x), Some(_y)] = { array } {}
The explanation
The current implementation of the borrow checker is AST-based while a future implementation will be MIR-based. At a high level, you can think of this as "working on the code as I typed it" (AST) and "working on the logical flow of data in my code" (MIR).
Certain "hacks" have been added to the AST borrow checker, which is why you can successfully use an array literal but not the variable. With the MIR borrow checker, bigger hacks like these will disappear and the borrow checker will also become more precise, allowing more code to compile.
It is unexpected that the third version does not compile. The same issue occurs when matching boxed values as described here.
Citing the explanation for the error from the linked answer:
My only guess is that the ownership of the Box is moved to the first
param, the param is extracted, then the compiler tries to move it
again to the next parameter.
Replace "Box" with "array" and you get an explanation for what is going on when matching an array. One of the solutions presented in the linked answer also works for matching arrays - the use of curly braces to force a complete move of the Box/array into the match expression:
fn consume(_: Box<u64>) {}
let array = [Some(Box::new(1)), Some(Box::new(2))];
match {array} {
[Some(x), Some(y)] => {
consume(x);
consume(y);
}
_ => (),
}

How do I check if a value is valid and then return it?

I am trying to receive some input, and then either return the value if it's valid or get input again if it's not. However, I'm running into borrow checker issues trying to both check the value and return it (new to Rust). Here's the code snippet:
fn get_move(&self) -> (String, String) {
let player = self.current_player();
let mut mv;
let mut is_valid = false;
loop {
mv = player.make_move();
{
is_valid = self.move_valid(mv);
}
match is_valid {
true => return mv,
_ => continue,
}
}
}
fn move_valid(&self, (_from,_to): (String, String)) -> bool {
false
}
Error returned is
error[E0382]: use of moved value: `mv`
--> src/game.rs:75:32
|
72 | is_valid = self.move_valid(mv);
| -- value moved here
...
75 | true => return mv,
| ^^ value used here after move
|
= note: move occurs because `mv` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait
I tried adding the scope around self.move_valid, but it the move_valid method still appears to be owning mv when I try and return it.
Is there a standard Rust pattern for repeated input until the value is valid? How can I get this to work?
When you just pass an unadorned parameter (like (String,String)) to a function, you are moving the value into that function. You don't have it anymore, you've given it to the function.
You probably just want to change the parameter type to &(String,String) to pass a reference instead. Then the function will be able to look at the data, but no ownership change will take place.

Returning a struct created by serde_json in a function

I'm stuck on what seems like a simple issue. I get why I am seeing the error but can't seem to resolve it. Obviously I am missing something fundamental.
fn terraform_deploy_info<'a>(app: &'a MyApp) -> std::result::Result<&MyAppDeployInfo, Error> {
let terraform = process::Command::new("terraform")
// We are querying output values.
.arg("output")
// We want it in json format for easy processing.
.arg("-json")
.output()
.expect("failed to execute terraform");
let output = String::from_utf8_lossy(&terraform.stdout);
let data: TerraformOutputs = serde_json::from_str(&output).unwrap();
let m = data.deploy_info.value.iter().filter(|&x| x.app == "myapp").collect::<Vec<_>>();
if m.len() > 1 {
return Err(Error::MultipleDeployInfo);
}
match m.get(0) {
Some(&x) => Ok(x),
None => Err(Error::NoDeployInfo),
}
}
The error I get is:
borrowed value must be valid for the lifetime 'a as defined on the body at
Which makes sense to me, as I am creating the struct in the function and returning a borrowed reference, which of course goes away when the function is finished.
But, when I change the return type be std::result::Result<MyAppDeployInfo, Error> (that is, not returning a reference) I can't seem to get Ok(x) to work...I get an error:
expected struct `MyAppDeployInfo`, found reference
Again, this makes sense as serde_json creates a structure and then I iterate through references, so when I index into the collection I am looking at a reference.
So I tried all sorts of things to get the struct value like dereferencing, Box::new, clone(), to_owned(), etc and still can't get it to work.
I've searched all the issues here, read the book, etc and it is still not clear to me how I can resolve this...any pointers would be appreciated.
Without knowing more about your project (please produce an MCVE next time), I'd say that you can change the .iter() call into .into_iter(). Instead of collecting into a Vec and then using get, I'd simply work with the iterator directly:
let m = data.deploy_info.value.into_iter().filter(|&x| x.app == "myapp").fuse();
match (m.next(), m.next()) {
(None, None) => Err(Error::NoDeployInfo),
(Some(x), None) => Ok(x),
(Some(_), Some(_)) => Err(Error::MultipleDeployInfo),
(None, Some(_)) => panic!("Iterator::fuse broken"),
}
Observe the types of your snippet.
let m = data.deploy_info.value // value is a Vec<MyAppDeployInfo>
.iter() // returns a Iterator<Item=&MyAppDeployInfo>
.filter(|&x| x.app == "myapp")
.collect::<Vec<_>>(); // collects into a Vec<&MyAppDeployInfo>
if m.len() > 1 {
return Err(Error::MultipleDeployInfo);
}
match m.get(0) { // get() returns a reference to an element
// i.e. a &&MyAppDeployInfo
Some(&x) // pattern match says x : &MyAppDeployInfo
=> Ok(x), // which matches the return type
// but you get a borrowing error.
None => Err(Error::NoDeployInfo),
}
}
Now if you change the return type to Result<MyAppDeployInfo, Error> as you should, you get the mismatched type issue, because x is a reference. If you dereference x, you get the error "cannot move out of borrowed content", because MyAppDeployInfo is not Copy, and you're trying to move. If you write x.clone(), it should work, unless you changed something else?
Alternatively, you can, from the beginning, work with moving content around. If you write data.deploy_info.value.into_iter().filter(|x| x.app == "myapp"), you move out of the initial structure instead of copying it. Then the resulting Vec will have MyAppDeployInfo as its item type. Then you could make it mut and use pop() instead of get(0) to get the only element in a way that you can move out of.
Or you can do what #ker recommended and not use collect() in the first place. I'd still switch to into_iter() though, making this the final code:
fn terraform_deploy_info(app: &MyApp) // no explicit lifetime needed
-> std::result::Result<MyAppDeployInfo, Error> {
let data = // ...
let mut m = data.deploy_info.value.into_iter()
.filter(|x| x.app == "myapp").fuse();
match (m.next(), m.next()) {
(None, None) => Err(Error::NoDeployInfo),
(Some(x), None) => Ok(x),
(Some(_), Some(_)) => Err(Error::MultipleDeployInfo),
(None, Some(_)) => panic!("Iterator::fuse broken"),
}
}

Resources