match控制流构造
match
控制流构造
Rust有一个非常强大的控制流构造,称为 match
,它允许您将一个值与一系列模式进行比较,然后根据匹配的模式执行代码。模式可以由字面值、变量名、通配符和许多其他内容组成;第18章介绍了所有不同类型的模式及其功能。match
的强大之处在于模式的表现力,以及编译器确认所有可能的情况都得到处理。
将 match
表达式类比为硬币分类机:硬币沿着带有不同大小孔的轨道滑动,每个硬币都会掉落到它所符合的第一个孔中。同样,值会通过 match
中的每个模式,并在第一个与之匹配的模式中,值“符合”,然后该值将掉入关联的代码块中,在执行期间使用。
说到硬币,让我们使用硬币 作为例子来使用 match
!我们可以编写一个函数,该函数接受一个未知的美国硬币,并类似于计数机一样,确定它是哪一种硬币,并以美分为单位返回其价值,如示例所示(见代码清单6-3)。
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
代码清单6-3:一个枚举和一个 match
表达式,其中枚举的变体是其模式
让我们来分解 value_in_cents
函数中的 match
。首先,我们列出 match
关键字,后面是一个表达式,在这种情况下是值 coin
。这似乎与与 if
一起使用的条件表达式非常相似,但有一个很大的区别:if
中的条件需要求值为布尔值,而在这里可以是任何类型。此示例中的 coin
类型是我们在第一行定义的 Coin
枚举类型。
接下来是 match
分支。分支由两部分组成:模式和一些代码。第一个分支在这里有一个模式,它是值 Coin::Penny
,然后是将模式和代码分开的 =>
运算符。在这种情况下,代码仅为值 1
。每个分支用逗号与下一个分开。
当 match
表达式执行时,它按顺序将结果值与每个分支的模式进行比较。如果某个模式与值匹配,将执行与该模式相关联的代码。如果该模式与值不匹配,则继续执行下一个分支,就像硬币分类机一样。我们可以有尽可能多的分支:在代码清单6-3中,我们的 match
有四个分支。
每个分支关联的代码是一个表达式,匹配分支中表达式的结果是整个 match
表达式的返回值。
通常,如果 match
分支的代码很短,我们不会使用花括号;就像在代码清单6-3中,每个分支仅返回一个值。如果您想在匹配分支中运行多行代码,必须使用花括号,并且接在分支后面的逗号则是可选的。例如,以下代码每次调用方法时都会打印“Lucky penny!”但仍然返回块的最后一个值 1
:
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
绑定到值的模式
match
分支的另一个有用功能是它们可以绑定到与模式匹配的值的部分。这是我们从枚举变体中提取值的方法。
举个例子,我们将一个枚举变体更改为在其内部保存数据。从1999年到2008年,美国铸造了每个州的25美分硬币,每一面有50个州的不同设计。其他硬币没有州设计,因此只有25美分硬币有这个额外值。我们可以通过将 Quarter
变体更改为包含 UsState
值来将此信息添加到我们的 enum
中,如代码清单6-4中所示。
#[derive(Debug)] // 这样我们可以检查状态
enum UsState {
Alabama,
Alaska,
// --snip--
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
代码清单6-4:一个 Coin
枚举,其中 Quarter
变体还包含一个 UsState
值
假设我们的朋友想要收集所有50个州的硬币。在将我们的零钱按硬币类型分类的同时,我们还会宣读与每个25美分硬币相关的州的名称,这样如果他们没有的州出现,他们就可以将其加入到他们的收藏中。
在此代码的 match
表达式中,我们在与变体 `Coin::
Quarter的值匹配的模式中添加了一个名为
state的变量。当
Coin::Quarter 匹配时,
state变量将绑定到该25美分硬币的州的值。然后,我们可以在该分支的代码中使用
state`,如下所示:
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("州25美分硬币来自{:?}!", state);
25
}
}
}
如果我们调用 value_in_cents(Coin::Quarter(UsState::Alaska))
,coin
将是 Coin::Quarter(UsState::Alaska)
。当我们将该值与每个 match
分支进行比较时,直到达到 Coin::Quarter(state)
时,没有一个匹配。在这一点上,state
的绑定将是值 UsState::Alaska
。然后,我们可以在 println!
表达式中使用该绑定,从而从 Quarter
变体的 Coin
枚举中获取内部州的值。
使用 Option
进行匹配
在前面的部分中,我们希望从 Option<T>
的 Some
情况中获取内部的 T
值;我们也可以像对待 Coin
枚举一样使用 match
来处理 Option<T>
!我们不再比较硬币,而是比较 Option<T>
的变体,但 match
表达式的工作方式保持不变。
假设我们想编写一个函数,它接受一个 Option<i32>
,如果内部有值,则将该值加1。如果内部没有值,则函数应返回 None
值,并且不尝试执行任何操作。
感谢 match
,编写这个函数非常容易,如代码清单6-5所示。
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
代码清单6-5:一个在 Option<i32>
上使用 match
表达式的函数
让我们更详细地查看第一个 plus_one
的执行。当我们调用 plus_one(five)
时,在 plus_one
函数体中,变量 x
将具有值 Some(5)
。然后,我们将其与每个 match
分支进行比较:
None => None,
Some(5)
的值不匹配模式 None
,因此我们继续到下一个分支:
Some(i) => Some(i + 1),
Some(5)
是否匹配 Some(i)