Reputation: 25058
I'm working on a compiler for a toy language and I want to be able to check for errors on each file. I have a MooFile
struct that has a Vec<anyhow::Error>
where I put errors when they are encountered. Now I want to go through the files, get the errors, and flatten that into a single vec to return from the compiler but I'm running up against the borrow checker. I'm going to post my various attempt but they keep running into similar issues.
fn compile(&mut self) -> Result<(), Vec<Error>> {
...
// Check for errors
let has_errors = self.files
.iter()
.map(|file| file.has_errors())
.reduce(|acc, value| acc | value)
.unwrap_or(false);
if has_errors {
let mut errors = vec![];
self.files
.iter()
.for_each(|&mut file| errors.append(&mut file.errors));
Err(errors)
} else {
Ok(())
}
}
error[E0596]: cannot borrow `file.errors` as mutable, as it is behind a `&` reference
--> src\lib.rs:62:48
|
62 | .for_each(|file| errors.append(&mut file.errors));
| ---- ^^^^^^^^^^^^^^^^ `file` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| help: consider changing this to be a mutable reference: `&mut MooFile`
fn compile(&mut self) -> Result<(), Vec<Error>> {
...
// Check for errors
let has_errors = self.files
.iter()
.map(|file| file.has_errors())
.reduce(|acc, value| acc | value)
.unwrap_or(false);
if has_errors {
let mut errors = vec![];
self.files
.into_iter()
.for_each(|mut file| errors.append(&mut file.errors));
Err(errors)
} else {
Ok(())
}
}
error[E0507]: cannot move out of `self.files` which is behind a mutable reference
--> src\lib.rs:63:13
|
63 | self.files
| ^^^^^^^^^^ move occurs because `self.files` has type `Vec<MooFile>`, which does not implement the `Copy` trait
64 | .into_iter()
| ----------- `self.files` moved due to this method call
fn compile(&mut self) -> Result<(), Vec<Error>> {
...
// Check for errors
let errors: Vec<Error> = self.files
.iter()
.map(|file| file.errors)
.flatten()
.collect();
if errors.len() > 0 {
Err(errors)
} else {
Ok(())
}
}
error[E0507]: cannot move out of `file.errors` which is behind a shared reference
--> src\lib.rs:54:25
|
54 | .map(|file| file.errors)
| ^^^^^^^^^^^ move occurs because `file.errors` has type `Vec<anyhow::Error>`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0507`.
error: could not compile `moo2` due to previous error
I feel like there has to be some kind of idiomatic way of doing this kind of thing that I'm missing.
Upvotes: 0
Views: 305
Reputation: 8980
Most of these errors come from how you create your iterators. If you assume x
is a Vec<Y>
:
x.iter()
: Consumes &x
and creates an iterator of &Y
.x.iter_mut()
: Consumes &mut x
and creates an iterator of &mut Y
.x.into_iter()
: Consumes ownership of the entire Vec<Y>
and creates an iterator of owned Y
.The first version uses .iter()
when you want a mutable reference (.iter_mut()
). The second uses .into_iter()
when you want a mutable reference (.iter_mut()
). And the third uses .iter()
when you attempt to claim ownership of the errors by disposing of the files (.into_iter()
, but you can't drop self.files
without issues).
Here is one way you could Clone
the errors without moving them out of each file
.
let mut errors = Vec::new();
self.files.iter()
.filter(|file| file.has_errors())
.foreach(|file| errors.extend_from_slice(&file.errors));
if has_errors {
return Err(errors)
}
Ok(())
Or you could drain
them into a new Vec
using extend
. This version also uses any
to look a bit cleaner, but is not as efficient.
if self.files.iter().any(|f| f.has_errors()) {
let mut errors = Vec::new();
for file in &mut self.files {
errors.extend(file.errors.drain(..));
}
return Err(errors)
}
Ok(())
Upvotes: 2