Означает ли это сообщение об ошибке, что я могу использовать сопоставление шаблонов для циклов?

Я не ожидаю, что следующий код будет работать, но в рамках изучения грамматики я попытался на игровой площадке:

fn main() {
    struct EOF {};
    let lines = vec![Ok("line 1"), Ok("line 2"), Err(EOF {})];
    for Ok(line) in lines {
        println!("{}", line);
    }
}

Сообщение об ошибке

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

Согласно вышеприведенному сообщению, мне кажется, что мне нужно только добавить руку для Err дела Err. Но какова правильная грамматика?

Ответ 1

Вы можете использовать шаблоны в качестве привязки в цикле for, но не шаблоны с опровержением. Здесь описывается различие между опровержимыми и неопровержимыми шаблонами, но суть в том, что если шаблон может потерпеть неудачу, его нельзя использовать в операторе let или цикле for. Если шаблон не может потерпеть неудачу, вы не можете (в настоящее время) использовать его в if let или while let. (Последнее может быть изменено в будущей версии, чтобы выдавать предупреждение вместо сбоя.)

Пример неопровержимого шаблона, используемого в цикле for, может выглядеть примерно так:

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) - неопровержимый шаблон, потому что в любом месте, где он проверяет тип, он будет совпадать. Здесь он проверяет тип, потому что элементы, которые перебираются (определены реализацией IntoIterator для &HashMap), являются кортежами. Вы также можете написать выше как

for tuple in &numbers {
    let (name, number) = tuple;
    println!("{}: {}", name, number);
}

потому что let - это другое место, где разрешены только неопровержимые образцы.

Ответ 2

Да, вы можете использовать шаблоны во многих местах, но не все из них позволяют условно переходить, когда существует несколько возможных шаблонов.

Цикл for - это место, где вы не можете добавлять условия. Вот что говорит вам ошибка с "опровержимым шаблоном": существует шаблон, который не будет обработан. Вместо этого вы в основном используете шаблон для выполнения деструктуризации переменной цикла:

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);
    }
}

Условный:

  • match
  • if let
  • while let

Безусловный:

  • for
  • let
  • параметры функции

Ответ 3

Но какова правильная грамматика?

Это дает результат, который вы хотите:

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);
    }
}

Обратите внимание, что здесь вы можете использовать flat_map потому что Result реализует метод into_iter предоставляемый признаком IntoIterator.

Это еще один вариант, 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);
        }
    }
}

Вы также можете остановить итерацию по делу Err:

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);
    }
}