模式在Rust中十分常见。我们在[变量绑定](Variable Bindings 变量绑定.md),[匹配语句](Match 匹配.md)和其它一些地方使用它们。让我们开始一个快速的关于模式可以干什么的教程!
快速回顾:你可以直接匹配常量,并且_作为“任何”类型:
_
let x = 1; match x { 1 => println!("one"), 2 => println!("two"), 3 => println!("three"), _ => println!("anything"), }
这会打印出one。
one
有一个模式的陷阱:就像任何引入一个新绑定的语句,他们会引入隐藏。例如:
let x = 1; let c = 'c'; match c { x => println!("x: {} c: {}", x, c), } println!("x: {}", x)
这会打印:
x: c c: c x: 1
换句话说,x =>匹配到了模式并引入了一个叫做x的新绑定。这个新绑定的作用域是匹配分支并拥有c的值。注意匹配作用域外的x的值对内部的x的值并无影响。因为我们已经有了一个x,新的x隐藏了它。
x =>
x
c
你可以使用|匹配多个模式:
|
let x = 1; match x { 1 | 2 => println!("one or two"), 3 => println!("three"), _ => println!("anything"), }
这会输出one or two。
one or two
如果你有一个复合数据类型,例如一个[结构体](Structs 结构体.md),你可以在模式中解构它:
struct Point { x: i32, y: i32, } let origin = Point { x: 0, y: 0 }; match origin { Point { x, y } => println!("({},{})", x, y), }
我们可以用:来给出一个不同的名字:
:
struct Point { x: i32, y: i32, } let origin = Point { x: 0, y: 0 }; match origin { Point { x: x1, y: y1 } => println!("({},{})", x1, y1), }
如果你只关心部分值,我们不需要给它们都命名:
struct Point { x: i32, y: i32, } let origin = Point { x: 0, y: 0 }; match origin { Point { x, .. } => println!("x is {}", x), }
这会输出x is 0。
x is 0
你可以对任何成员进行这样的匹配,不仅仅是第一个:
struct Point { x: i32, y: i32, } let origin = Point { x: 0, y: 0 }; match origin { Point { y, .. } => println!("y is {}", y), }
这会输出y is 0。
y is 0
这种“解构”行为可以用在任何复合数据类型上,例如[元组](Primitive Types 原生类型.md#元组(tuples))和[枚举](Enums 枚举.md)
你可以在模式中使用_来忽视它的类型和值。例如,这是一个Result<T, E>的match:
Result<T, E>
match
# let some_value: Result<i32, &'static str> = Err("There was an error"); match some_value { Ok(value) => println!("got a value: {}", value), Err(_) => println!("an error occurred"), }
在第一个分支,我们绑定了Ok变量中的值为value,不过在Err分支,我们用_来忽视特定的错误,而只是打印了一个通用的错误信息。
Ok
value
Err
_在任何创建绑定的模式中都有效。这在忽略一个大大结构体的部分字段时很有用:
fn coordinate() -> (i32, i32, i32) { // generate and return some sort of triple tuple # (1, 2, 3) } let (x, _, z) = coordinate();
这里,我们绑定元组第一个和最后一个元素为x和z,不过省略了中间的元素。
z
相似的,你可以在模式中用..来忽略多个值。
..
enum OptionalTuple { Value(i32, i32, i32), Missing, } let x = OptionalTuple::Value(5, -2, 3); match x { OptionalTuple::Value(..) => println!("Got a tuple!"), OptionalTuple::Missing => println!("No such luck."), }
这会打印Got a tuple!。
Got a tuple!
ref
ref mut
如果你想要一个引用,使用ref关键字:
let x = 5; match x { ref r => println!("Got a reference to {}", r), }
这会输出Got a reference to 5。
Got a reference to 5
这里,match中的r是&i32类型的。换句话说,ref关键字创建了一个在模式中使用的引用。如果你需要一个可变引用,ref mut同样可以做到:
r
&i32
let mut x = 5; match x { ref mut mr => println!("Got a mutable reference to {}", mr), }
你可以用...匹配一个范围的值:
...
let x = 1; match x { 1 ... 5 => println!("one through five"), _ => println!("anything"), }
这会输出one through five。
one through five
范围经常用在整数和char上。
char
let x = '?'; match x { 'a' ... 'j' => println!("early letter"), 'k' ... 'z' => println!("late letter"), _ => println!("something else"), }
这会输出something else。
something else
你可以使用@把值绑定到名字上:
@
let x = 1; match x { e @ 1 ... 5 => println!("got a range element {}", e), _ => println!("anything"), }
这会输出got a range element 1。在你想对一个复杂数据结构进行部分匹配的时候,这个特性十分有用:
got a range element 1
#[derive(Debug)] struct Person { name: Option<String>, } let name = "Steve".to_string(); let mut x: Option<Person> = Some(Person { name: Some(name) }); match x { Some(Person { name: ref a @ Some(_), .. }) => println!("{:?}", a), _ => {} }
这会输出 Some("Steve"),因为我们把Person里面的name绑定到a。
Some("Steve")
name
a
如果你在使用|的同时也使用了@,你需要确保名字在每个模式的每一部分都绑定名字:
let x = 5; match x { e @ 1 ... 5 | e @ 8 ... 10 => println!("got a range element {}", e), _ => println!("anything"), }
你可以用if来引入匹配守卫(match guards):
if
enum OptionalInt { Value(i32), Missing, } let x = OptionalInt::Value(5); match x { OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"), OptionalInt::Value(..) => println!("Got an int!"), OptionalInt::Missing => println!("No such luck."), }
这会输出Got an int!。
Got an int!
如果你在if中使用多重模式,if条件将适用于所有模式:
let x = 4; let y = false; match x { 4 | 5 if y => println!("yes"), _ => println!("no"), }
这会打印no,因为if适用于整个4 | 5,而不仅仅是5,换句话说,if语句的优先级是这样的:
no
4 | 5
5
(4 | 5) if y => ...
而不是这样:
4 | (5 if y) => ...
(口哨)!根据你的需求,你可以对上面的多种匹配方法进行组合:
match x { Foo { x: Some(ref name), y: None } => ... }
模式十分强大。好好使用它们。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8