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也不会警告我们存在没有使用的变量。