Как мне ослабить неисчерпывающую проверку шаблонов на наличие вложенных совпадений в известных вариантах? - PullRequest
1 голос
/ 09 мая 2019

Как мне убедить компилятор Rust в том, что внутреннее выражение match здесь хорошо, поскольку внешнее match уже ограничило возможные типы?

enum Op {
    LoadX,
    LoadY,
    Add,
}

fn test(o: Op) {
    match o {
        Op::LoadX | Op::LoadY => {
            // do something common with them for code reuse:
            print!("Loading ");

            // do something specific to each case:
            match o {
                // now I know that `o` can only be LoadX | LoadY,
                // but how to persuade the compiler?
                Op::LoadX => print!("x"), /* LoadX specific */
                Op::LoadY => print!("y"), /* LoadY specific */
                _ => panic!("shouldn't happen!"),
            }

            println!("...");
        }

        Op::Add => println!("Adding"),
    }
}

fn main() {
    test(Op::LoadX);
    test(Op::LoadY);
    test(Op::Add);
}

Я пробовал два подхода, но ни одинКажется, работает.

  1. Назовите шаблон or, а затем сопоставьте его с этим именем:

    match o {
        load@(Op::LoadX | Op::LoadY) => {
        // ...
        match load {
            // ...
        }
    } 
    

    Это недопустимый синтаксис Rust.

  2. Назовите и свяжите каждого конструктора:

    match o {
        load@Op::LoadX | load@Op::LoadY => {
        // ...
        match load {
           //...
        }
    } 
    

    Это все еще не удовлетворяет проверке исчерпываемости, следовательно, то же сообщение об ошибке:

    error[E0004]: non-exhaustive patterns: `Add` not covered
      --> src/main.rs:14:19
       |
    14 |             match load {
       |                   ^ pattern `Add` not covered
    
    

Есть какой-то идиоматический способ решения этой проблемы, или я должен просто поставить panic!("shouldn't happen") повсюду или реструктурировать код?

Rust link link

Ответы [ 2 ]

3 голосов
/ 09 мая 2019

Я думаю, что вам просто нужно провести рефакторинг вашего кода, очевидно, LoadX и LoadY очень близки. Поэтому я думаю, что вы должны создать второе перечисление, которое перегруппирует их:

enum Op {
    Load(State),
    Add,
}

enum State {
    X,
    Y,
}

fn test(o: Op) {
    match o {
        Op::Load(state) => {
            // do something common with them for code reuse
            print!("Loading ");

            // do something specific to each case:
            match state {
                State::X => print!("x"),
                State::Y => print!("y"),
            }

            println!("...");
        }

        Op::Add => println!("Adding"),
    }
}

fn main() {
    test(Op::Load(State::X));
    test(Op::Load(State::Y));
    test(Op::Add);
}

Это имеет больше смысла для меня. Я думаю, что это лучший способ выразить то, что вы хотите.

1 голос
/ 09 мая 2019

Вы не можете. Концептуально, ничто не мешает вам делать o = Op::Add между внешним совпадением и внутренним совпадением. Для варианта вполне возможно переключаться между двумя матчами.

Я бы, вероятно, следовал коду Stargateur , но если вы не хотите реструктурировать свой enum, помните, что в Rust есть несколько методов абстракции. Например, функции довольно хороши для повторного использования кода, а замыкания (или признаки) хороши для настройки логики.

enum Op {
    LoadX,
    LoadY,
    Add,
}

fn load<R>(f: impl FnOnce() -> R) {
    print!("Loading ");
    f();
    println!("...");
}

fn test(o: Op) {
    match o {
        Op::LoadX => load(|| print!("x")),
        Op::LoadY => load(|| print!("y")),
        Op::Add => println!("Adding"),
    }
}

fn main() {
    test(Op::LoadX);
    test(Op::LoadY);
    test(Op::Add);
}

Должен ли я просто поставить panic!("shouldn't happen")

Вы должны использовать unreachable! вместо panic!, так как это более семантически правильно для программиста.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...