Home

Rust 2.9 匹配

11 Jan 2015 by LelouchHe

原文链接

简单的if/else一般是不够的,因为我们经常需要多于2个的选择.而且,else的情况有时会非常复杂,此时该怎么办呢?

Rust有一个match关键字,用来替代复杂的if/else.如下:

let x = 5;
match x {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    4 => println!("four"),
    5 => println!("five"),
    _ => println!("something else"),
}

match后接一个表达式,然后根据表达式的值进行选择.每个分支都是val => expression的格式.当值匹配时,相应的表达式就会求值.这是一种模式匹配(pattern matching),所以该关键字称为match.

这样做的优点是什么?首先,match强制保证了穷尽检查(exhaustiveness checking).请看上面的最后一个下划线(“_“)的分支,如果我们去掉这个,Rust会编译出错:

error: non-exhaustive patterns: `_` not covered

换句话说,Rust告诉我们存在遗漏的情况.因为x是一个整数,Rust知道它还有一些其他的值,比如6.如果没有_的话,这个情况就没法覆盖了,所以Rust就无法编译通过._充当了”其余所有”的分支.如果其他分支都没有匹配,_的分支就会执行,只要有了这个,我们就对所有的x的取值都有了匹配的分支,所以我们的程序就能编译通过了.

match还能解析enum.还记得上节的代码么?

use std::cmp::Ordering;

fn cmp(a: i32, b: i32) -> Ordering {
    if a < b { Ordering::Less }
    else if a > b { Ordering::Greater }
    else { Ordering::Equal }
}

fn main() {
    let x = 5;
    let y = 10;

    let ordering = cmp(x, y);

    if ordering == Ordering::Less {
        println!("less");
    } else if ordering == Ordering::Greater {
        println!("greater");
    } else {
        println!("equal");
    }
}

我们可以这样重写:

use std::cmp::Ordering;

fn cmp(x: i32, y: i32) -> Ordering {
    if a < b { Ordering::Less }
    else if a > b { Ordering::Greater }
    else { Ordering::Equal }
}

fn main() {
    let x = 5;
    let y = 10;

    match cmp(x, y) {
        Ordering::Less => println!("less");
        Ordering::Greater => println!("less");
        Ordering::Equal => println!("less");
    }
}

这个版本的代码噪音更少,而且将Ordering的所有情况都覆盖了.要是使用if/else,我们遗漏了一些情况(比如Ordering::Greater),程序可以编译通过的.但使用了match,程序无法编译通过.Rust可以保证我们覆盖了所有的情况.

match表达式还能获取包含在枚举类型中的值,比如:

enum OptionalInt {
    Value(i32),
    Missing,
}

fn main() {
    let x = OptionalInt::Value(5);
    let y = OptionalInt::Missing;

    match x {
        OptionalInt::Value(n) => println!("x is {}", n),
        OptionalInt::Missing  => println!("x is missing!"),
    }
    match y {
        OptionalInt::Value(n) => println!("y is {}", n),
        OptionalInt::Missing  => println!("y is missing!"),
    }
}

这就是如何获取枚举类型中的值.这也让我们可以处理错误或非预期的错误;比如,一个函数无法保证会返回整型(这里的i32),就可以返回OptionalInt,我们可以通过match来处理.可以看到,枚举和match在一起是非常有用的.

match也是一个表达式,我们可以把它放到let绑定的右边,或者任意需要表达式的地方.我们可以这样来实现上面的例子:

use std::cmp::Ordering;

fn cmp(x: i32, y: i32) -> Ordering {
    if a < b { Ordering::Less }
    else if a > b { Ordering::Greater }
    else { Ordering::Equal }
}

fn main() {
    let x = 5;
    let y = 10;

    println!("{}", match cmp(x, y) {
        Ordering::Less => "less",
        Ordering::Greater => "greater",
        Ordering::Equal => "equal",
    });
}

有时,这是个非常有用的模式.