12、初识Rust – match控制流

目录 编程

学过javascript或者lua这些编程语言的话,就会知道这些语言里都有switch这个方法。不过Rust中没有switch,当我们遇到需要使用switch的环境时,可以使用match来进行控制。

match是rust中非常常见也是重要的一个方法,与switch不同,rust编译器对match有非常不错的检查,它确保了所有可能的情况都得到处理。

enum Abc {
    A,
    B,
    C,
}

fn main() {
    println!("{},{},{}",
    test_function(Abc::A),
    test_function(Abc::B),
    test_function(Abc::C));
}

fn test_function(abc: Abc) -> u8 {
    match abc {
        Abc::A => 1,
        Abc::B => 2,
        Abc::C => 3,
    }
}

----------------------------------------------------
   Compiling master v0.1.0 (/Users/xxxxx)
    Finished dev [unoptimized + debuginfo] target(s) in 0.45s
     Running `target/debug/master`
1,2,3

如上的例子,将枚举Abc的成员传入函数test_function 并在其中进行match控制,遇到符合的分支就进行返回。每一个分支关联的代码就是一个表达式,表达式的结果就作为整个match的返回值。

如果分支代码较多,就可以使用{}进行包裹。match匹配分支的另一个功能就是可以绑定匹配模式的部分值,从而可以从枚举成员中提取值。

#[derive(Debug)]
enum Def{
    D,
    E,
    F,
}
enum Abc {
    A,
    B,
    C,
    Next(Def),
}

fn main() {
    test_function(Abc::A);
    test_function(Abc::B);
    test_function(Abc::C);
    test_function(Abc::Next(Def::D));
    test_function(Abc::Next(Def::E));
    test_function(Abc::Next(Def::F));
}

fn test_function(abc: Abc) -> u8 {
    match abc {
        Abc::A => 1,
        Abc::B => 2,
        Abc::C => 3,
        Abc::Next(tmp) => {
            println!("{:?}",tmp);
            5
        }
    }
}
---------------------------------------------------------
   Compiling master v0.1.0 (/Users/xxxxx)
    Finished dev [unoptimized + debuginfo] target(s) in 0.47s
     Running `target/debug/master`
D
E
F

如上,当传入 test_function(Abc::Next(Def::D)) 时,match就会将它和分支相比较,直到遇到Abc::Next(tmp)这时就会打印出匹配到的Abc::Next(Def::D)。

match与Option<T>可以非常好的配合,从上一篇文章学习的Option<T>后可以得知,Option<T>包含Some(T)和None,match处理Option<T>和处理枚举没什么区别只不过匹配的不在是枚举的键名,而是匹配Option<T>的成员。

fn main() {
    let a = Some(5);
    let b:Option<i32> = None;
    println!("{:?},{:?}",test_function(a),test_function(b))
}

fn test_function(z: Option<i32>) -> Option<i32> {
    match z {
        Some(value) => Some(value),
        None => None,
    }
}
------------------------------------------------------------------
   Compiling master v0.1.0 (/Users/xxxxxxx)
    Finished dev [unoptimized + debuginfo] target(s) in 0.57s
     Running `target/debug/master`
Some(5),None

需要注意的是,Rust中match的匹配是穷尽的,例如下面这段代码,它会导致编译器无法编译。

fn main() {
    let a = Some(5);
    let b:Option<i32> = None;
    println!("{:?},{:?}",test_function(a),test_function(b))
}

fn test_function(z: Option<i32>) -> Option<i32> {
    match z {
        Some(value) => Some(value),
    }
}
------------------------------------------------------
   Compiling master v0.1.0 (/Users/xxxxxx)
error[E0004]: non-exhaustive patterns: `None` not covered
   --> src/main.rs:9:11
    |
9   |     match z {
    |           ^ pattern `None` not covered
    |
note: `Option<i32>` defined here
   --> /Users/xxx/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs:522:5
    |
518 | / pub enum Option<T> {
519 | |     /// No value.
520 | |     #[lang = "None"]
521 | |     #[stable(feature = "rust1", since = "1.0.0")]
522 | |     None,
    | |     ^^^^ not covered
...   |
526 | |     Some(#[stable(feature = "rust1", since = "1.0.0")] T),
527 | | }
    | |_-
    = note: the matched value is of type `Option<i32>`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
10  ~         Some(value) => Some(value),
11  ~         None => todo!(),
    |

For more information about this error, try `rustc --explain E0004`.
error: could not compile `master` due to previous error

rust中match必须穷举到最后的可能性来保证代码运行时的安全性,避免出现一些意外之外的错误。

但是,如果我们要对一定量的值采取默认操作,比如程序运行时抽取一个1-5的随机数,对1,2,3进行x * x处理,对4,5进行统一的 乘3处理。这时我们可以使用通配模式和_占位符来实现。

fn main() {
    println!("{}",test_function(1));
    println!("{}",test_function(2));
    println!("{}",test_function(3));
    println!("{}",test_function(4));
    println!("{}",test_function(5));
}

fn test_function(value: i8) -> i8 {
    match value {
        1 => 1 * 1,
        2 => 2 * 2,
        3 => 3 * 3,
        other => {
            other * 3
        }
    }
}
-------------------------------------------------
   Compiling master v0.1.0 (/Users/xxxx)
    Finished dev [unoptimized + debuginfo] target(s) in 0.55s
     Running `target/debug/master`
1
4
9
12
15

如上 other分支的代码将传递给它对应的代码块,处理完成后返回。虽然我们没有列出所有的i8可能的值,但是这段代码还是可以成功编译,因为最后一个模式会接收到所有没有列出的特殊值,不过需要注意的是,other分支后面不可以带其它分支,因为该分支永远不可能被匹配到。

fn main() {
    println!("{}",test_function(1));
    println!("{}",test_function(2));
    println!("{}",test_function(3));
    println!("{}",test_function(4));
    println!("{}",test_function(5));
}

fn test_function(value: i8) -> i8 {
    match value {
        1 => 1 * 1,
        2 => 2 * 2,
        3 => 3 * 3,
        _ => {
            8
        }
    }
}
-----------------------------------------------
   Compiling master v0.1.0 (/Users/xxxxx)
    Finished dev [unoptimized + debuginfo] target(s) in 0.50s
     Running `target/debug/master`
1
4
9
8
8

当你需要只处理1-3而略过4-5的值时 _ => 是一个非常不错的方法,它可以为你匹配值但却不绑定任何值,这表示我们告知Rust我们不需要使用这个值,同时Rust也不会警告我们存在没有使用的变量。