如何展平许多嵌套层的模式匹配?

How to flatten many nested layers of pattern matches?

我的代码要求枚举必须是特定类型才能继续(如果不是,那么这是一个不可恢复的错误)。用 if let 模式表达这一点会导致大量缩进和语法噪音。这是一个例子:

enum Fruit {
    Apple(i32),
    Banana(i32, i32),
}

enum Veggie {
    Tomato(bool),
    Pepper(i32),
}

fn test() -> i32 {
    let fruit = fruit_producing_func();
    if let Fruit::Apple(x) = fruit {
        let veggie = veggie_producing_func(x);
        if let Veggie::Pepper(y) = veggie {
            y
        } else {
            panic!();
        }
    } else {
        panic!();
    }
}

在我的代码中,这个嵌套比 2 层大得多。是否有更简洁的方式来表达这一点,避免创建额外的块,甚至避免重复 panic! 语句?简单地做:

let Fruit::Apple(x) = fruit;

导致错误,因为 let 绑定必须是详尽无遗的(我希望它会在匹配失败时恐慌)。

您可以将检查重构为枚举本身的便捷方法,然后这将使您的 test 函数扁平化:

enum Fruit {
    Apple(i32),
    Banana(i32, i32),
}

impl Fruit {
    // panics if not apple
    fn get_apple(&self) -> i32 {
        if let Fruit::Apple(x) = self {
            *x
        } else {
            panic!()
        }
    }
}

enum Veggie {
    Tomato(bool),
    Pepper(i32),
}

impl Veggie {
    // panics if not pepper
    fn get_pepper(&self) -> i32 {
        if let Veggie::Pepper(y) = self {
            *y
        } else {
            panic!()
        }
    }
}

fn fruit_producing_func() -> Fruit {
    todo!()
}

fn veggie_producing_func(x: i32) -> Veggie {
    todo!()
}

fn test() -> i32 {
    let fruit = fruit_producing_func();
    let x = fruit.get_apple();
    let veggie = veggie_producing_func(x);
    let y = veggie.get_pepper();
    y
}

playground

您可以改用 match 将其展平:

fn test() -> i32 {
    let fruit = fruit_producing_func();
    let x = match fruit {
        Fruit::Apple(x) => x,
        _ => panic!(),
    };
    let veggie = veggie_producing_func(x);
    let y = match veggie {
        Veggie::Pepper(y) => y,
        _ => panic!(),
    };
    y
}

或者,您可以在关联函数中实现匹配,然后使用常用的 Option 习语处理该结果。

impl Fruit {
    fn apple(&self) -> Option<i32> {
        if let Fruit::Apple(x) = self {
            Some(*x)
        } else {
            None
        }
    }
}

impl Veggie {
    fn pepper(&self) -> Option<i32> {
        if let Veggie::Pepper(y) = self {
            Some(*y)
        } else {
            None
        }
    }
}
fn test() -> i32 {
    let fruit = fruit_producing_func();
    let x =  fruit.apple().unwrap();
    let veggie = veggie_producing_func(x);
    veggie.pepper().expect("Tomato is a fruit after all")
}