ch18-03-pattern-syntax.md >
commit e72de80f114dc68f69f3920768314f7c005d0dd5
通過本書我們已領(lǐng)略過許多不同類型模式的例子。在本節(jié)中,我們收集了模式中所有有效的語法,并討論為什么以及何時你可能要使用這些語法。
如第六章所示,可以直接匹配字面值模式。如下代碼給出了一些例子:
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}
這段代碼會打印 one
因為 x
的值是 1。如果希望代碼獲得特定的具體值,則該語法很有用。
命名變量是匹配任何值的不可反駁模式,這在之前已經(jīng)使用過數(shù)次。然而當其用于 match
表達式時情況會有些復雜。因為 match
會開始一個新作用域,match
表達式中作為模式的一部分聲明的變量會覆蓋 match
結(jié)構(gòu)之外的同名變量,與所有變量一樣。在示例 18-11 中,聲明了一個值為 Some(5)
的變量 x
和一個值為 10
的變量 y
。接著在值 x
上創(chuàng)建了一個 match
表達式。觀察匹配分支中的模式和結(jié)尾的 println!
,并在運行此代碼或進一步閱讀之前推斷這段代碼會打印什么。
文件名: src/main.rs
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
Some(y) => println!("Matched, y = {:?}", y),
_ => println!("Default case, x = {:?}", x),
}
println!("at the end: x = {:?}, y = {:?}", x, y);
示例 18-11: 一個 match
語句其中一個分支引入了覆蓋變量 y
讓我們看看當 match
語句運行的時候發(fā)生了什么。第一個匹配分支的模式并不匹配 x
中定義的值,所以代碼繼續(xù)執(zhí)行。
第二個匹配分支中的模式引入了一個新變量 y
,它會匹配任何 Some
中的值。因為我們在 match
表達式的新作用域中,這是一個新變量,而不是開頭聲明為值 10 的那個 y
。這個新的 y
綁定會匹配任何 Some
中的值,在這里是 x
中的值。因此這個 y
綁定了 x
中 Some
內(nèi)部的值。這個值是 5,所以這個分支的表達式將會執(zhí)行并打印出 Matched, y = 5
。
如果 x
的值是 None
而不是 Some(5)
,頭兩個分支的模式不會匹配,所以會匹配下劃線。這個分支的模式中沒有引入變量 x
,所以此時表達式中的 x
會是外部沒有被覆蓋的 x
。在這個假想的例子中,match
將會打印 Default case, x = None
。
一旦 match
表達式執(zhí)行完畢,其作用域也就結(jié)束了,同理內(nèi)部 y
的作用域也結(jié)束了。最后的 println!
會打印 at the end: x = Some(5), y = 10
。
為了創(chuàng)建能夠比較外部 x
和 y
的值,而不引入覆蓋變量的 match
表達式,我們需要相應(yīng)地使用帶有條件的匹配守衛(wèi)(match guard)。我們稍后將在 “匹配守衛(wèi)提供的額外條件” 這一小節(jié)討論匹配守衛(wèi)。
在 match
表達式中,可以使用 |
語法匹配多個模式,它代表 或(or)的意思。例如,如下代碼將 x
的值與匹配分支相比較,第一個分支有 或 選項,意味著如果 x
的值匹配此分支的任一個值,它就會運行:
let x = 1;
match x {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("anything"),
}
上面的代碼會打印 one or two
。
..=
語法允許你匹配一個閉區(qū)間范圍內(nèi)的值。在如下代碼中,當模式匹配任何在此范圍內(nèi)的值時,該分支會執(zhí)行:
let x = 5;
match x {
1..=5 => println!("one through five"),
_ => println!("something else"),
}
如果 x
是 1、2、3、4 或 5,第一個分支就會匹配。這相比使用 |
運算符表達相同的意思更為方便;相比 1..=5
,使用 |
則不得不指定 1 | 2 | 3 | 4 | 5
。相反指定范圍就簡短的多,特別是在希望匹配比如從 1 到 1000 的數(shù)字的時候!
范圍只允許用于數(shù)字或 char
值,因為編譯器會在編譯時檢查范圍不為空。char
和 數(shù)字值是 Rust 僅有的可以判斷范圍是否為空的類型。
如下是一個使用 char
類型值范圍的例子:
let x = 'c';
match x {
'a'..='j' => println!("early ASCII letter"),
'k'..='z' => println!("late ASCII letter"),
_ => println!("something else"),
}
Rust 知道 c
位于第一個模式的范圍內(nèi),并會打印出 early ASCII letter
。
也可以使用模式來解構(gòu)結(jié)構(gòu)體、枚舉和元組,以便使用這些值的不同部分。讓我們來分別看一看。
示例 18-12 展示帶有兩個字段 x
和 y
的結(jié)構(gòu)體 Point
,可以通過帶有模式的 let
語句將其分解:
文件名: src/main.rs
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 0, y: 7 };
let Point { x: a, y: b } = p;
assert_eq!(0, a);
assert_eq!(7, b);
}
示例 18-12: 解構(gòu)一個結(jié)構(gòu)體的字段為單獨的變量
這段代碼創(chuàng)建了變量 a
和 b
來匹配結(jié)構(gòu)體 p
中的 x
和 y
字段。這個例子展示了模式中的變量名不必與結(jié)構(gòu)體中的字段名一致。不過通常希望變量名與字段名一致以便于理解變量來自于哪些字段。
因為變量名匹配字段名是常見的,同時因為 let Point { x: x, y: y } = p;
包含了很多重復,所以對于匹配結(jié)構(gòu)體字段的模式存在簡寫:只需列出結(jié)構(gòu)體字段的名稱,則模式創(chuàng)建的變量會有相同的名稱。示例 18-13 展示了與示例 18-12 有著相同行為的代碼,不過 let
模式創(chuàng)建的變量為 x
和 y
而不是 a
和 b
:
文件名: src/main.rs
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 0, y: 7 };
let Point { x, y } = p;
assert_eq!(0, x);
assert_eq!(7, y);
}
示例 18-13: 使用結(jié)構(gòu)體字段簡寫來解構(gòu)結(jié)構(gòu)體字段
這段代碼創(chuàng)建了變量 x
和 y
,與變量 p
中的 x
和 y
相匹配。其結(jié)果是變量 x
和 y
包含結(jié)構(gòu)體 p
中的值。
也可以使用字面值作為結(jié)構(gòu)體模式的一部分進行解構(gòu),而不是為所有的字段創(chuàng)建變量。這允許我們測試一些字段為特定值的同時創(chuàng)建其他字段的變量。
示例 18-14 展示了一個 match
語句將 Point
值分成了三種情況:直接位于 x
軸上(此時 y = 0
為真)、位于 y
軸上(x = 0
)或不在任何軸上的點。
文件名: src/main.rs
fn main() {
let p = Point { x: 0, y: 7 };
match p {
Point { x, y: 0 } => println!("On the x axis at {}", x),
Point { x: 0, y } => println!("On the y axis at {}", y),
Point { x, y } => println!("On neither axis: ({}, {})", x, y),
}
}
示例 18-14: 解構(gòu)和匹配模式中的字面值
第一個分支通過指定字段 y
匹配字面值 0
來匹配任何位于 x
軸上的點。此模式仍然創(chuàng)建了變量 x
以便在分支的代碼中使用。
類似的,第二個分支通過指定字段 x
匹配字面值 0
來匹配任何位于 y
軸上的點,并為字段 y
創(chuàng)建了變量 y
。第三個分支沒有指定任何字面值,所以其會匹配任何其他的 Point
并為 x
和 y
兩個字段創(chuàng)建變量。
在這個例子中,值 p
因為其 x
包含 0 而匹配第二個分支,因此會打印出 On the y axis at 7
。
本書之前的部分曾經(jīng)解構(gòu)過枚舉,比如第六章中示例 6-5 中解構(gòu)了一個 Option<i32>
。一個當時沒有明確提到的細節(jié)是解構(gòu)枚舉的模式需要對應(yīng)枚舉所定義的儲存數(shù)據(jù)的方式。讓我們以示例 6-2 中的 Message
枚舉為例,編寫一個 match
使用模式解構(gòu)每一個內(nèi)部值,如示例 18-15 所示:
文件名: src/main.rs
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let msg = Message::ChangeColor(0, 160, 255);
match msg {
Message::Quit => {
println!("The Quit variant has no data to destructure.")
}
Message::Move { x, y } => {
println!(
"Move in the x direction {} and in the y direction {}",
x, y
);
}
Message::Write(text) => println!("Text message: {}", text),
Message::ChangeColor(r, g, b) => println!(
"Change the color to red {}, green {}, and blue {}",
r, g, b
),
}
}
示例 18-15: 解構(gòu)包含不同類型值成員的枚舉
這段代碼會打印出 Change the color to red 0, green 160, and blue 255
。嘗試改變 msg
的值來觀察其他分支代碼的運行。
對于像 Message::Quit
這樣沒有任何數(shù)據(jù)的枚舉成員,不能進一步解構(gòu)其值。只能匹配其字面值 Message::Quit
,因此模式中沒有任何變量。
對于像 Message::Move
這樣的類結(jié)構(gòu)體枚舉成員,可以采用類似于匹配結(jié)構(gòu)體的模式。在成員名稱后,使用大括號并列出字段變量以便將其分解以供此分支的代碼使用。這里使用了示例 18-13 所展示的簡寫。
對于像 Message::Write
這樣的包含一個元素,以及像 Message::ChangeColor
這樣包含三個元素的類元組枚舉成員,其模式則類似于用于解構(gòu)元組的模式。模式中變量的數(shù)量必須與成員中元素的數(shù)量一致。
目前為止,所有的例子都只匹配了深度為一級的結(jié)構(gòu)體或枚舉。當然也可以匹配嵌套的項!
例如,我們可以重構(gòu)列表 18-15 的代碼來同時支持 RGB 和 HSV 色彩模式:
enum Color {
Rgb(i32, i32, i32),
Hsv(i32, i32, i32),
}
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(Color),
}
fn main() {
let msg = Message::ChangeColor(Color::Hsv(0, 160, 255));
match msg {
Message::ChangeColor(Color::Rgb(r, g, b)) => println!(
"Change the color to red {}, green {}, and blue {}",
r, g, b
),
Message::ChangeColor(Color::Hsv(h, s, v)) => println!(
"Change the color to hue {}, saturation {}, and value {}",
h, s, v
),
_ => (),
}
}
示例 18-16: 匹配嵌套的枚舉
match
表達式第一個分支的模式匹配一個包含 Color::Rgb
枚舉成員的 Message::ChangeColor
枚舉成員,然后模式綁定了 3 個內(nèi)部的 i32
值。第二個分支的模式也匹配一個 Message::ChangeColor
枚舉成員, 但是其內(nèi)部的枚舉會匹配 Color::Hsv
枚舉成員。我們可以在一個 match
表達式中指定這些復雜條件,即使會涉及到兩個枚舉。
甚至可以用復雜的方式來混合、匹配和嵌套解構(gòu)模式。如下是一個復雜結(jié)構(gòu)體的例子,其中結(jié)構(gòu)體和元組嵌套在元組中,并將所有的原始類型解構(gòu)出來:
let ((feet, inches), Point { x, y }) = ((3, 10), Point { x: 3, y: -10 });
這將復雜的類型分解成部分組件以便可以單獨使用我們感興趣的值。
通過模式解構(gòu)是一個方便利用部分值片段的手段,比如結(jié)構(gòu)體中每個單獨字段的值。
有時忽略模式中的一些值是有用的,比如 match
中最后捕獲全部情況的分支實際上沒有做任何事,但是它確實對所有剩余情況負責。有一些簡單的方法可以忽略模式中全部或部分值:使用 _
模式(我們已經(jīng)見過了),在另一個模式中使用 _
模式,使用一個以下劃線開始的名稱,或者使用 ..
忽略所剩部分的值。讓我們來分別探索如何以及為什么要這么做。
我們已經(jīng)使用過下劃線(_
)作為匹配但不綁定任何值的通配符模式了。雖然 _
模式作為 match
表達式最后的分支特別有用,也可以將其用于任意模式,包括函數(shù)參數(shù)中,如示例 18-17 所示:
文件名: src/main.rs
fn foo(_: i32, y: i32) {
println!("This code only uses the y parameter: {}", y);
}
fn main() {
foo(3, 4);
}
示例 18-17: 在函數(shù)簽名中使用 _
這段代碼會完全忽略作為第一個參數(shù)傳遞的值 3
,并會打印出 This code only uses the y parameter: 4
。
大部分情況當你不再需要特定函數(shù)參數(shù)時,最好修改簽名不再包含無用的參數(shù)。在一些情況下忽略函數(shù)參數(shù)會變得特別有用,比如實現(xiàn) trait 時,當你需要特定類型簽名但是函數(shù)實現(xiàn)并不需要某個參數(shù)時。此時編譯器就不會警告說存在未使用的函數(shù)參數(shù),就跟使用命名參數(shù)一樣。
也可以在一個模式內(nèi)部使用_
忽略部分值,例如,當只需要測試部分值但在期望運行的代碼中沒有用到其他部分時。示例 18-18 展示了負責管理設(shè)置值的代碼。業(yè)務(wù)需求是用戶不允許覆蓋現(xiàn)有的自定義設(shè)置,但是可以取消設(shè)置,也可以在當前未設(shè)置時為其提供設(shè)置。
let mut setting_value = Some(5);
let new_setting_value = Some(10);
match (setting_value, new_setting_value) {
(Some(_), Some(_)) => {
println!("Can't overwrite an existing customized value");
}
_ => {
setting_value = new_setting_value;
}
}
println!("setting is {:?}", setting_value);
示例 18-18: 當不需要 Some
中的值時在模式內(nèi)使用下劃線來匹配 Some
成員
這段代碼會打印出 Can't overwrite an existing customized value
接著是 setting is Some(5)
。在第一個匹配分支,我們不需要匹配或使用任一個 Some
成員中的值;重要的部分是需要測試 setting_value
和 new_setting_value
都為 Some
成員的情況。在這種情況,我們打印出為何不改變 setting_value
,并且不會改變它。
對于所有其他情況(setting_value
或 new_setting_value
任一為 None
),這由第二個分支的 _
模式體現(xiàn),這時確實希望允許 new_setting_value
變?yōu)?nbsp;setting_value
。
也可以在一個模式中的多處使用下劃線來忽略特定值,如示例 18-19 所示,這里忽略了一個五元元組中的第二和第四個值:
let numbers = (2, 4, 8, 16, 32);
match numbers {
(first, _, third, _, fifth) => {
println!("Some numbers: {}, {}, {}", first, third, fifth)
}
}
示例 18-19: 忽略元組的多個部分
這會打印出 Some numbers: 2, 8, 32
,值 4 和 16 會被忽略。
如果你創(chuàng)建了一個變量卻不在任何地方使用它,Rust 通常會給你一個警告,因為這可能會是個 bug。但是有時創(chuàng)建一個還未使用的變量是有用的,比如你正在設(shè)計原型或剛剛開始一個項目。這時你希望告訴 Rust 不要警告未使用的變量,為此可以用下劃線作為變量名的開頭。示例 18-20 中創(chuàng)建了兩個未使用變量,不過當編譯代碼時只會得到其中一個的警告:
文件名: src/main.rs
fn main() {
let _x = 5;
let y = 10;
}
示例 18-20: 以下劃線開始變量名以便去掉未使用變量警告
這里得到了警告說未使用變量 y
,不過沒有警告說未使用下劃線開頭的變量。
注意,只使用 _
和使用以下劃線開頭的名稱有些微妙的不同:比如 _x
仍會將值綁定到變量,而 _
則完全不會綁定。為了展示這個區(qū)別的意義,示例 18-21 會產(chǎn)生一個錯誤。
let s = Some(String::from("Hello!"));
if let Some(_s) = s {
println!("found a string");
}
println!("{:?}", s);
示例 18-21: 以下劃線開頭的未使用變量仍然會綁定值,它可能會獲取值的所有權(quán)
我們會得到一個錯誤,因為 s
的值仍然會移動進 _s
,并阻止我們再次使用 s
。然而只使用下劃線本身,并不會綁定值。示例 18-22 能夠無錯編譯,因為 s
沒有被移動進 _
:
let s = Some(String::from("Hello!"));
if let Some(_) = s {
println!("found a string");
}
println!("{:?}", s);
示例 18-22: 單獨使用下劃線不會綁定值
上面的代碼能很好的運行;因為沒有把 s
綁定到任何變量;它沒有被移動。
對于有多個部分的值,可以使用 ..
語法來只使用部分并忽略其它值,同時避免不得不每一個忽略值列出下劃線。..
模式會忽略模式中剩余的任何沒有顯式匹配的值部分。在示例 18-23 中,有一個 Point
結(jié)構(gòu)體存放了三維空間中的坐標。在 match
表達式中,我們希望只操作 x
坐標并忽略 y
和 z
字段的值:
struct Point {
x: i32,
y: i32,
z: i32,
}
let origin = Point { x: 0, y: 0, z: 0 };
match origin {
Point { x, .. } => println!("x is {}", x),
}
示例 18-23: 通過使用 ..
來忽略 Point
中除 x
以外的字段
這里列出了 x
值,接著僅僅包含了 ..
模式。這比不得不列出 y: _
和 z: _
要來得簡單,特別是在處理有很多字段的結(jié)構(gòu)體,但只涉及一到兩個字段時的情形。
..
會擴展為所需要的值的數(shù)量。示例 18-24 展示了元組中 ..
的應(yīng)用:
文件名: src/main.rs
fn main() {
let numbers = (2, 4, 8, 16, 32);
match numbers {
(first, .., last) => {
println!("Some numbers: {}, {}", first, last);
}
}
}
示例 18-24: 只匹配元組中的第一個和最后一個值并忽略掉所有其它值
這里用 first
和 last
來匹配第一個和最后一個值。..
將匹配并忽略中間的所有值。
然而使用 ..
必須是無歧義的。如果期望匹配和忽略的值是不明確的,Rust 會報錯。示例 18-25 展示了一個帶有歧義的 ..
例子,因此其不能編譯:
文件名: src/main.rs
fn main() {
let numbers = (2, 4, 8, 16, 32);
match numbers {
(.., second, ..) => {
println!("Some numbers: {}", second)
},
}
}
示例 18-25: 嘗試以有歧義的方式運用 ..
如果編譯上面的例子,會得到下面的錯誤:
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
error: `..` can only be used once per tuple pattern
--> src/main.rs:5:22
|
5 | (.., second, ..) => {
| -- ^^ can only be used once per tuple pattern
| |
| previously used here
error: could not compile `patterns` due to previous error
Rust 不可能決定在元組中匹配 second
值之前應(yīng)該忽略多少個值,以及在之后忽略多少個值。這段代碼可能表明我們意在忽略 2
,綁定 second
為 4
,接著忽略 8
、16
和 32
;抑或是意在忽略 2
和 4
,綁定 second
為 8
,接著忽略 16
和 32
,以此類推。變量名 second
對于 Rust 來說并沒有任何特殊意義,所以會得到編譯錯誤,因為在這兩個地方使用 ..
是有歧義的。
匹配守衛(wèi)(match guard)是一個指定于 match
分支模式之后的額外 if
條件,它也必須被滿足才能選擇此分支。匹配守衛(wèi)用于表達比單獨的模式所能允許的更為復雜的情況。
這個條件可以使用模式中創(chuàng)建的變量。示例 18-26 展示了一個 match
,其中第一個分支有模式 Some(x)
還有匹配守衛(wèi) if x < 5
:
let num = Some(4);
match num {
Some(x) if x < 5 => println!("less than five: {}", x),
Some(x) => println!("{}", x),
None => (),
}
示例 18-26: 在模式中加入匹配守衛(wèi)
上例會打印出 less than five: 4
。當 num
與模式中第一個分支比較時,因為 Some(4)
匹配 Some(x)
所以可以匹配。接著匹配守衛(wèi)檢查 x
值是否小于 5
,因為 4
小于 5
,所以第一個分支被選擇。
相反如果 num
為 Some(10)
,因為 10 不小于 5 所以第一個分支的匹配守衛(wèi)為假。接著 Rust 會前往第二個分支,這會匹配因為它沒有匹配守衛(wèi)所以會匹配任何 Some
成員。
無法在模式中表達類似 if x % 2 == 0
的條件,所以通過匹配守衛(wèi)提供了表達類似邏輯的能力。這種替代表達方式的缺點是,編譯器不會嘗試為包含匹配守衛(wèi)的模式檢查窮盡性。
在示例 18-11 中,我們提到可以使用匹配守衛(wèi)來解決模式中變量覆蓋的問題,那里 match
表達式的模式中新建了一個變量而不是使用 match
之外的同名變量。新變量意味著不能夠測試外部變量的值。示例 18-27 展示了如何使用匹配守衛(wèi)修復這個問題。
文件名: src/main.rs
fn main() {
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
Some(n) if n == y => println!("Matched, n = {}", n),
_ => println!("Default case, x = {:?}", x),
}
println!("at the end: x = {:?}, y = {}", x, y);
}
示例 18-27: 使用匹配守衛(wèi)來測試與外部變量的相等性
現(xiàn)在這會打印出 Default case, x = Some(5)
?,F(xiàn)在第二個匹配分支中的模式不會引入一個覆蓋外部 y
的新變量 y
,這意味著可以在匹配守衛(wèi)中使用外部的 y
。相比指定會覆蓋外部 y
的模式 Some(y)
,這里指定為 Some(n)
。此新建的變量 n
并沒有覆蓋任何值,因為 match
外部沒有變量 n
。
匹配守衛(wèi) if n == y
并不是一個模式所以沒有引入新變量。這個 y
正是 外部的 y
而不是新的覆蓋變量 y
,這樣就可以通過比較 n
和 y
來表達尋找一個與外部 y
相同的值的概念了。
也可以在匹配守衛(wèi)中使用 或 運算符 |
來指定多個模式,同時匹配守衛(wèi)的條件會作用于所有的模式。示例 18-28 展示了結(jié)合匹配守衛(wèi)與使用了 |
的模式的優(yōu)先級。這個例子中重要的部分是匹配守衛(wèi) if y
作用于 4
、5
和 6
,即使這看起來好像 if y
只作用于 6
:
let x = 4;
let y = false;
match x {
4 | 5 | 6 if y => println!("yes"),
_ => println!("no"),
}
示例 18-28: 結(jié)合多個模式與匹配守衛(wèi)
這個匹配條件表明此分支值匹配 x
值為 4
、5
或 6
同時 y
為 true
的情況。運行這段代碼時會發(fā)生的是第一個分支的模式因 x
為 4
而匹配,不過匹配守衛(wèi) if y
為假,所以第一個分支不會被選擇。代碼移動到第二個分支,這會匹配,此程序會打印出 no
。這是因為 if
條件作用于整個 4 | 5 | 6
模式,而不僅是最后的值 6
。換句話說,匹配守衛(wèi)與模式的優(yōu)先級關(guān)系看起來像這樣:
(4 | 5 | 6) if y => ...
而不是:
4 | 5 | (6 if y) => ...
可以通過運行代碼時的情況看出這一點:如果匹配守衛(wèi)只作用于由 |
運算符指定的值列表的最后一個值,這個分支就會匹配且程序會打印出 yes
。
at 運算符(@
)允許我們在創(chuàng)建一個存放值的變量的同時測試其值是否匹配模式。示例 18-29 展示了一個例子,這里我們希望測試 Message::Hello
的 id
字段是否位于 3..=7
范圍內(nèi),同時也希望能將其值綁定到 id_variable
變量中以便此分支相關(guān)聯(lián)的代碼可以使用它??梢詫?nbsp;id_variable
命名為 id
,與字段同名,不過出于示例的目的這里選擇了不同的名稱。
enum Message {
Hello { id: i32 },
}
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello {
id: id_variable @ 3..=7,
} => println!("Found an id in range: {}", id_variable),
Message::Hello { id: 10..=12 } => {
println!("Found an id in another range")
}
Message::Hello { id } => println!("Found some other id: {}", id),
}
示例 18-29: 使用 @
在模式中綁定值的同時測試它
上例會打印出 Found an id in range: 5
。通過在 3..=7
之前指定 id_variable @
,我們捕獲了任何匹配此范圍的值并同時測試其值匹配這個范圍模式。
第二個分支只在模式中指定了一個范圍,分支相關(guān)代碼沒有一個包含 id
字段實際值的變量。id
字段的值可以是 10、11 或 12,不過這個模式的代碼并不知情也不能使用 id
字段中的值,因為沒有將 id
值保存進一個變量。
最后一個分支指定了一個沒有范圍的變量,此時確實擁有可以用于分支代碼的變量 id
,因為這里使用了結(jié)構(gòu)體字段簡寫語法。不過此分支中沒有像頭兩個分支那樣對 id
字段的值進行測試:任何值都會匹配此分支。
使用 @
可以在一個模式中同時測試和保存變量值。
模式是 Rust 中一個很有用的功能,它幫助我們區(qū)分不同類型的數(shù)據(jù)。當用于 match
語句時,Rust 確保模式會包含每一個可能的值,否則程序?qū)⒉荒芫幾g。let
語句和函數(shù)參數(shù)的模式使得這些結(jié)構(gòu)更強大,可以在將值解構(gòu)為更小部分的同時為變量賦值??梢詣?chuàng)建簡單或復雜的模式來滿足我們的要求。
接下來,在本書倒數(shù)第二章中,我們將介紹一些 Rust 眾多功能中較為高級的部分。
更多建議: