Rust match mutable enum reference with vectors - vector

I'm trying to change an enum's named property but getting this error.
cannot move out of a mutable referencerustc(E0507)
parser.rs(323, 36): data moved here
parser.rs(323, 36): move occurs because `statements` has type `std::vec::Vec<std::boxed::Box<ast::ast::StatementType>>`, which does not implement the `Copy` trait
I saw that we can change enum's named props with match statements. But I couldn't understand why there's a move occurrence, since I'm borrowing the enum itself. Here's the code:
match &mut block {
StatementType::Block { mut statements, .. } => {
statements = block_statements;
},
_ => panic!()
};
return block;
I've tried mem::swap too but still it's the same error:
match &mut block {
StatementType::Block { mut statements, .. } => {
// statements = block_statements;
std::mem::swap(&mut statements, &mut block_statements);
},
_ => panic!()
};
return block;
BUT when I do this:
std::mem::swap(&mut *statements, &mut *block_statements);
The error changes to:
the size for values of type `[std::boxed::Box<ast::ast::StatementType>]` cannot be known at compilation time
doesn't have a size known at compile-time
Types are:
StatementType is an enum that derives Clone
Block is mutable variable of StatementType
Block's statements is a variable of Vec<Box<StatementType>>
block_statements is another variable of Vec<Box<StatementType>>
Please do not say that it happens because statements' type is Vector: come with a solution as I can read error messages too.

You have to think what the type of statements is and what you would like it to be.
With the code as you wrote it, it is of type Vec<_> (sorry, I said it), but since the match captures the block by reference, it cannot take the contents by value, hence the error. Note that the error is not in the assignment but in the match brace itself:
error[E0507]: cannot move out of a mutable reference
--> src/main.rs:15:11
|
15 | match &mut block {
| ^^^^^^^^^^
16 | StatementType::Block { mut statements, .. } => {
| --------------
| |
| data moved here
| move occurs because `statements` has type `std::vec::Vec<std::boxed::Box<StatementType>>`, which does not implement the `Copy` trait
You would like statement to be of type &mut Vec<_> of course. And you get that by using the ref mut capture mode:
match block {
StatementType::Block { ref mut statements, .. } => {
*statements = block_statements;
},
_ => panic!()
};
And remember to use *statement when assigning, as it is now a reference. You could also use a mem::swap if you want, of course:
std::mem::swap(statements, &mut block_statements);
But note that you do not need to match &mut block but you can do match block directly.
There is this thing called match ergonomics that lets you match against a reference and omit the ref mut capture mode, that makes your code easier to write and understand:
match &mut block {
StatementType::Block { statements, .. } => {
*statements = block_statements;
},
_ => panic!()
};
The problem in your original code is that if specify any capture mode then match ergonomics is disabled.

Related

How do I modify a Vector based on information from an item in the Vector?

How can I modify a Vec based on information from an item within the Vec without having both immutable and mutable references to the vector?
I've tried to create a minimal example that demonstrates my specific problem. In my real code, the Builder struct is already the intermediate struct that other answers propose. Specifically, I do not think this question is answered by other questions because:
Why does refactoring by extracting a method trigger a borrow checker error? - What would I put in the intermediate struct? I'm not calculating a separate value from Vec<Item>. The value being modified/operated on is Vec<Item> and is what would need to be in the intermediate struct
Suppose I have a list of item definitions, where items are either a String, a nested list of Items, or indicate that a new item should be added to the list of items being processed:
enum Item {
Direct(String),
Nested(Vec<Item>),
New(String),
}
There is also a builder that holds a Vec<Item> list, and builds an item at the specified index:
struct Builder {
items: Vec<Item>,
}
impl Builder {
pub fn build_item(&mut self, item: &Item, output: &mut String) {
match item {
Item::Direct(v) => output.push_str(v),
Item::Nested(v) => {
for sub_item in v.iter() {
self.build_item(sub_item, output);
}
}
Item::New(v) => self.items.push(Item::Direct(v.clone())),
}
}
pub fn build(&mut self, idx: usize, output: &mut String) {
let item = self.items.get(idx).unwrap();
self.build_item(item, output);
}
}
This doesn't compile due to the error:
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/main.rs:26:9
|
25 | let item = self.items.get(idx).unwrap();
| ---------- immutable borrow occurs here
26 | self.build_item(item, output);
| ^^^^^----------^^^^^^^^^^^^^^
| | |
| | immutable borrow later used by call
| mutable borrow occurs here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0502`.
I don't know how to have the Builder struct be able to modify its items (i.e. have a mutable reference to self.items) based on information contained in one of the items (i.e. an immutable borrow of self.items).
Here is a playground example of the code.
Using Clone
#Stargateur recommended that I try cloning the item in build(). While this does work, I've been trying to not clone items for performance reasons. UPDATE: Without adding the Vec<Item> modification feature with Item::New, I implemented the clone() method in my real code and cloned the value in the equivalent of the example build() method above. I saw a 12x decrease in performance when I do self.items.get(idx).unwrap().clone() vs self.items.get(idx).unwrap(). I will continue looking for other solutions. The problem is, I'm still relatively new to Rust and am not sure how to bend the rules/do other things even with unsafe code.
Code that does work (playground)
impl Clone for Item {
fn clone(&self) -> Self {
match self {
Item::Direct(v) => Item::Direct(v.clone()),
Item::Nested(v) => Item::Nested(v.clone()),
Item::New(v) => Item::New(v.clone()),
}
}
}
and change build to clone the item first:
let item = self.items.get(idx).unwrap().clone();
Whenever approaching problems like this (which you will encounter relatively frequently while using Rust), the main goal should be to isolate the code requiring the immutable borrow from the code requiring the mutable borrow. If the borrow from the items vec in build is unavoidable (i.e. you cannot move the item out of self.items or copy/clone it) and you must pass in a reference to this item to build_item, you might want to consider rewriting your build_item function to not mutate self. In this case, build_item only ever appends new items to the end of self.items, which lets us make an interesting refactor: Rather than have build_item modify items, make it return the items to be added to the original vector, and then have the caller add the newly generated items to the items vector.
impl Builder {
fn generate_items(&self, item: &Item, output: &mut String) -> Vec<Item> {
match item {
Item::Direct(v) => {
output.push_str(v);
Vec::new()
}
Item::Nested(v) => {
v.iter()
.flat_map(|sub_item| self.generate_items(sub_item, output))
.collect()
}
Item::New(v) => vec![Item::Direct(v.clone())],
}
}
pub fn build_item(&mut self, item: &Item, output: &mut String) {
let mut new_items = self.generate_items(item, output);
self.items.append(&mut new_items);
}
pub fn build(&mut self, idx: usize, output: &mut String) {
// Non lexical lifetimes allow this to compile, as the compiler
// realizes that `item` borrow can be dropped before the mutable borrow
// Immutable borrow of self starts here
let item = self.items.get(idx).unwrap();
let mut new_items = self.generate_items(item, output);
// Immutable borrow of self ends here
// Mutable borrow of self starts here
self.items.append(&mut new_items);
}
}
Note that in order to preserve the API, your build_item function has been renamed to generate_items, and a new build_item function that uses generate_items has been created.
If you look carefully, you'll notice that generate_items doesn't even require self, and can be a free-standing function or a static function in Builder.
Playground

SliceIndex trait bound unsatisfied error when trying to index into a vector in Rust

I have a struct:
pub struct SomeCollection<'a> {
items: Vec<&'a SomeValue>,
name_mapping: HashMap<&'a str, usize>,
index_mapping: HashMap<&'a str, usize>
}
impl<'a> Index for SomeCollection<'a> {
type Output = Option<&'a SomeValue>;
fn index(&self, value_name: &str) -> &Self::Output {
match self.name_mapping.get(value_name) {
Some(index) => &self.items.get(index),
None => match self.index_mapping.get(value_name) {
Some(index) => &self.items.get(index),
None => &None
}
}
}
}
When I try compiling this code I get the following error:
error[E0277]: the trait bound `&usize: std::slice::SliceIndex<[&SomeValue]>` is not satisfied
--> src\some_collection.rs:49:48
|
49 | Some(index) => &self.items.get(index),
| ^^^ slice indices are of
type `usize` or ranges of `usize`
|
= help: the trait `std::slice::SliceIndex<[&SomeValue]>` is not implemented for `&usize`
Rust seems to be telling me that I can't index into a vector with index, which is a usize. I'm not sure why I need to implement this trait as it should be implemented for the default vector already. Can anyone enlighten me as to the real reason I'm getting this error? There may be other errors in this code that haven't surfaced yet so please bear that in mind when answering.
Maybe the compiler error is hard to understand, but at least it is accurate. It tells you, that index is a reference &usize however a Vec can only be indexed with the value usize. So all you have to do is dereference index.
Moreover, usually indexing panics if the key/index could not be found. (That's why the standard library offers the Vec::get, and HashMap::get as separate methods alongside indexing, which do not panic but return None if the given key/index not in the collection.)
impl<'a> Index<&str> for SomeCollection<'a> {
type Output = &'a SomeValue;
fn index(&self, value_name: &str) -> &Self::Output {
match self.name_mapping.get(value_name) {
Some(index) => &self.items[*index],
None => match self.index_mapping.get(value_name) {
Some(index) => &self.items[*index],
None => panic!("Missing key: {:?}", value_name),
}
}
}
}
Rust seems to be telling me that I can't index into a vector with index, which is a usize.
No, the compiler is telling you that you can't index a vector with an &usize.
the trait `std::slice::SliceIndex<[&SomeValue]>` is not implemented for `&usize`
Simply using *index instead will fix this particular problem. However, your code has other problems as well:
The Index trait needs a type parameter, e.g. &str in this case.
The index() method can generally only return a reference to data stored in the data structure you are indexing. Your code creates a temporary Option, which is not stored in the original data, and then tries to return a reference to that. If this is what you want to do, you need to define your own method (or trait) that returns the Option by value instead.

Folding over references inside a match results in a lifetime error

I want to build a string s by iterating over a vector of simple structs, appending different strings to acc depending on the struct.
#[derive(Clone, Debug)]
struct Point(Option<i32>, Option<i32>);
impl Point {
fn get_first(&self) -> Option<i32> {
self.0
}
}
fn main() {
let mut vec = vec![Point(None, None); 10];
vec[5] = Point(Some(1), Some(1));
let s: String = vec.iter().fold(
String::new(),
|acc, &ref e| acc + match e.get_first() {
None => "",
Some(ref content) => &content.to_string()
}
);
println!("{}", s);
}
Running this code results in the following error:
error: borrowed value does not live long enough
Some(ref content) => &content.to_string()
^~~~~~~~~~~~~~~~~~~
note: reference must be valid for the expression at 21:22...
|acc, &ref e| acc + match e.get_first() {
^
note: ...but borrowed value is only valid for the expression at 23:33
Some(ref content) => &content.to_string()
^~~~~~~~~~~~~~~~~~~~
The problem is that the lifetime of the &str I create seems to end immediately. However, if to_string() would have returned a &str in the first place, the compiler would not have complained. Then, what is the difference?
How can I make the compiler understand that I want the string references to live as long as I am constructing s?
There is a difference between the result of your branches:
"" is of type &'static str
content is of type i32, so you are converting it to a String and then from that to a &str... but this &str has the same lifetime as the String returned by to_string, which dies too early
A quick work-around, as mentioned by #Dogbert, is to move acc + inside the branches:
let s: String = vec.iter().fold(
String::new(),
|acc, &ref e| match e.get_first() {
None => acc,
Some(ref content) => acc + &content.to_string(),
}
);
However, it's a bit wasteful, because every time we have an integer, we are allocating a String (via to_string) just to immediately discard it.
A better solution is to use the write! macro instead, which just appends to the original string buffer. This means there are no wasted allocations.
use std::fmt::Write;
let s = vec.iter().fold(
String::new(),
|mut acc, &ref e| {
if let Some(ref content) = e.get_first() {
write!(&mut acc, "{}", content).expect("Should have been able to format!");
}
acc
}
);
It's maybe a bit more complicated, notably because formatting adds in error handling, but is more efficient as it only uses a single buffer.
There are multiple solutions to your problem. But first some explanations:
If to_string() would have returned a &str in the first place, the compiler would not have complained. Then, what is the difference?
Suppose there is a method to_str() that returns a &str. What would the signature look like?
fn to_str(&self) -> &str {}
To better understand the issue, lets add explicit lifetimes (that are not necessary thanks to lifetime elision):
fn to_str<'a>(&'a self) -> &'a str {}
It becomes clear that the returned &str lives as long as the receiver of the method (self). This would be OK since the receiver lives long enough for your acc + ... operation. In your case however, the .to_string() call creates a new object the only lives in the second match arm. After the arm's body is left, it will be destroyed. Therefore you can't pass a reference to it to the outer scope (in which acc + ... takes place).
So one possible solution looks like this:
let s = vec.iter().fold(
String::new(),
|acc, e| {
acc + &e.get_first()
.map(|f| f.to_string())
.unwrap_or(String::new())
}
);
It's not optimal, but luckily your default value is an empty string and the owned version of an empty string (String::new()) does not require any heap allocations, so there is no performance penalty.
However, we are still allocating once per integer. For a more efficient solution, see Matthieu M.'s answer.

How can I handle "if may be missing an else clause" when using an if inside of a map?

I'm working on a code challenge which will detect case-insensitive anagrams of a given word from a list of words.
My first cut is to use something like this:
pub fn anagrams_for(s: &'static str, v: &[&'static str]) -> Vec<&'static str> {
let mut outputs: Vec<&str> = vec![];
// Find the case-insensitive, sorted word to check
let mut s_sorted: Vec<_> = s.to_string().to_lowercase().chars().collect();
s_sorted.sort();
for word in v {
// Case-desensitize and sort each word in the slice
let mut word_sorted: Vec<_> = word.to_string().to_lowercase().chars().collect();
word_sorted.sort();
// if the case-insensitive words are the same post sort and not presort (to avoid self-anagrams), add it to the vector
if word_sorted == s_sorted && s.to_string().to_lowercase() != word.to_string().to_lowercase() {
outputs.push(word)
}
}
outputs
}
This works as expected, but is not very idiomatic. I'm now trying a second iteration which uses more functional features of Rust:
pub fn anagrams_for(s: &'static str, v: &[&'static str]) -> Vec<&'static str> {
let mut s_sorted: Vec<_> = s.to_string().to_lowercase().chars().collect();
s_sorted.sort();
v.iter().map(&|word: &str| {
let mut word_sorted: Vec<_> = word.to_string().to_lowercase().chars().collect();
word_sorted.sort();
if word_sorted == s_sorted && s.to_string().to_lowercase() != word.to_string().to_lowercase() {
word
}
}).collect()
}
I'm currently getting a few errors (most of which I could likely resolve), but the one I'm interested in solving is
if may be missing an else clause:
expected `()`,
found `&str`
(expected (),
found &-ptr) [E0308]
This is because in the case of a non-anagram, map attempts to push something into the vector (seemingly ()).
How can I handle this? It's possible that map isn't the best idiom because it requires some operation to be performed on each element in a list, not a subset (maybe filter?).
As you noticed, the problem is that in the non-anagram-case your closure (the || { ... } block) doesn't return a value.
You can solve this by using filter_map instead of map. That function takes a closure that returns Option<U> instead of U, so the last expression of your closure looks something like:
if /* ... */ {
Some(word)
} else {
None
}
Unrelated to the main question, some notes on your code:
You can remove the .to_string() calls before .to_lowercase() calls. the latter method belongs to the type str, so it works fine. Calling to_string() adds unnecessary allocations.
the & in front of the closure (&|...|) can most probably be removed...
... as can the : &str type annotation in the closures argument list

Why does asserting on the result of Deref::deref fail with a type mismatch?

The following is the Deref example from The Rust Programming Language except I've added another assertion.
Why does the assert_eq with the deref also equal 'a'? Why do I need a * once I've manually called deref?
use std::ops::Deref;
struct DerefExample<T> {
value: T,
}
impl<T> Deref for DerefExample<T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
fn main() {
let x = DerefExample { value: 'a' };
assert_eq!('a', *x.deref()); // this is true
// assert_eq!('a', x.deref()); // this is a compile error
assert_eq!('a', *x); // this is also true
println!("ok");
}
If I uncomment the line, I get this error:
error[E0308]: mismatched types
--> src/main.rs:18:5
|
18 | assert_eq!('a', x.deref());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected char, found &char
|
= note: expected type `char`
found type `&char`
= help: here are some functions which might fulfill your needs:
- .to_ascii_lowercase()
- .to_ascii_uppercase()
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
First, let's spell out the generic types for your specific example: 'a' is char, so we have:
impl Deref for DerefExample<char> {
type Target = char;
fn deref(&self) -> &char {
&self.value
}
}
Notably, the return type of deref is a reference to a char. Thus it shouldn't be surprising that, when you use just x.deref(), the result is a &char rather than a char. Remember, at that point deref is just another normal method — it's just implicitly invoked as part of some language-provided special syntax. *x, for example, will call deref and dereference the result, when applicable. x.char_method() and fn_taking_char(&x) will also call deref some number of times and then do something further with the result.
Why does deref return a reference to begin with, you ask? Isn't that circular? Well, no, it isn't circular: it reduces library-defined smart pointers to the built-in type &T which the compiler already knows how to dereference. By returning a reference instead of a value, you avoid a copy/move (which may not always be possible!) and allow &*x (or &x when it's coerced) to refer to the actual char that DerefExample holds rather than a temporary copy.
See also:
Why is the return type of Deref::deref itself a reference?

Resources