Does this error message mean I can use pattern mat

2020-02-13 07:55发布

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?

标签: rust
3条回答
The star\"
2楼-- · 2020-02-13 08:16

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:

  • match
  • if let
  • while let

Unconditional:

  • for
  • let
  • function parameters
查看更多
三岁会撩人
3楼-- · 2020-02-13 08:16

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);
    }
}
查看更多
▲ chillily
4楼-- · 2020-02-13 08:24

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.

查看更多
登录 后发表回答