dyi*_*ynx 5 pattern-matching rust non-exhaustive-patterns
我如何说服 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);
}
Run Code Online (Sandbox Code Playgroud)
我尝试了两种方法,但似乎都不起作用。
命名 or 模式,然后使用该名称进行匹配:
match o {
load@(Op::LoadX | Op::LoadY) => {
// ...
match load {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
这不是有效的 Rust 语法。
命名并绑定每个构造函数:
match o {
load@(Op::LoadX | Op::LoadY) => {
// ...
match load {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
这仍然不满足详尽性检查,因此出现相同的错误消息:
match o {
load@Op::LoadX | load@Op::LoadY => {
// ...
match load {
//...
}
}
Run Code Online (Sandbox Code Playgroud)有没有解决这个问题的惯用方法,或者我应该把panic!("shouldn't happen")所有地方都放在一起或重组代码?
你不能。o = Op::Add从概念上讲,没有什么可以阻止您在外部匹配和内部匹配之间进行操作。变体完全有可能在两场比赛之间发生变化。
我可能会遵循Stargateur 的代码,但如果您不想重组枚举,请记住 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);
}
Run Code Online (Sandbox Code Playgroud)
我应该把
panic!("shouldn't happen")
您应该使用unreachable!而不是panic!因为它对程序员来说在语义上更正确。