如何展平许多嵌套层的模式匹配?
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
}
您可以改用 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")
}
我的代码要求枚举必须是特定类型才能继续(如果不是,那么这是一个不可恢复的错误)。用 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
}
您可以改用 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")
}