I do not expect the following code to work, but as part of grammar exploration, I tried in playground:
fn main() {
struct EOF {};
let lines = vec![Ok("line 1"), Ok("line 2"), Err(EOF {})];
for Ok(line) in lines {
println!("{}", line);
}
}
The error message is
error[E0005]: refutable pattern in `for` loop binding: `Err(_)` not covered
--> src/main.rs:4:9
|
4 | for Ok(line) in lines {
| ^^^^^^^^ pattern `Err(_)` not covered
According to the message above it looks like I only need to add a match arm for the Err
case. But what is the right grammar to do so?
You can use patterns as the binding in a for
loop, but not refutable patterns. The difference between refutable and irrefutable patterns is described here, but the gist of it is, if a pattern could fail, you can't use it in a let
statement or a for
loop. If a pattern can't fail, you can't (currently) use it in if let
or while let
. (This last may be changed in a future version to issue a warning instead of failing.)
An example of an irrefutable pattern being used in a for
loop might be something like this:
let mut numbers = HashMap::new();
numbers.insert("one", 1);
numbers.insert("two", 2);
numbers.insert("three", 3);
for (name, number) in &numbers {
println!("{}: {}", name, number);
}
(name, number)
is an irrefutable pattern, because any place where it type checks, it will match. It type checks here because the items being iterated over (defined by the implementation of IntoIterator
for &HashMap
) are tuples. You could also write the above as
for tuple in &numbers {
let (name, number) = tuple;
println!("{}: {}", name, number);
}
because let
is another place where only irrefutable patterns are allowed.
Yes, you can use patterns in many places, but not all of them allow you to conditionally branch when there are multiple possible patterns.
A for
loop is one place where you cannot add conditions. That's what the error is telling you with "refutable pattern": there's a pattern that will not be handled. Instead, you mostly use the pattern to perform destructuring of the loop variable:
struct Thing {
foo: u8,
}
fn main() {
let things = vec![Thing { foo: 1 }, Thing { foo: 2 }, Thing { foo: 3 }];
for Thing { foo } in things {
println!("{}", foo);
}
}
Conditional:
Unconditional:
for
let
- function parameters
But what is the right grammar to do so?
This gets the result you want:
fn main() {
struct EOF;
let lines = vec![Ok("line 1"), Ok("line 2"), Err(EOF)];
for line in lines.into_iter().flat_map(|e| e) {
println!("{}", line);
}
}
Note that you can use flat_map
here because Result
implements the into_iter
method provided by the IntoIterator
trait.
This is another option using if let
:
fn main() {
struct EOF;
let lines = vec![Ok("line 1"), Ok("line 2"), Err(EOF)];
for result in lines {
if let Ok(line) = result {
println!("{}", line);
}
}
}
You may also want to stop iteration on an Err
case:
fn main() {
struct EOF;
let lines = vec![Ok("line 1"), Ok("line 2"), Err(EOF), Ok("line 3") ];
let mut lines_iter = lines.into_iter();
while let Some(Ok(line)) = lines_iter.next() {
println!("{}", line);
}
}