• 模式
    • 多重模式(Multiple patterns)
    • 解构(Destructuring)
    • 忽略绑定(Ignoring bindings)
    • refref mut
    • 范围(Ranges)
    • 绑定
    • 守卫(Guards)
    • 混合与匹配(Mix and Match)

    模式

    patterns.md


    commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2

    模式在Rust中十分常见。我们在变量绑定,匹配表达式和其它一些地方使用它们。让我们开始一个快速的关于模式可以干什么的教程!

    快速回顾:你可以直接匹配常量,并且_作为“任何”类型:

    1. let x = 1;
    2. match x {
    3. 1 => println!("one"),
    4. 2 => println!("two"),
    5. 3 => println!("three"),
    6. _ => println!("anything"),
    7. }

    这会打印出one

    可以在任何分支创建值的绑定:

    1. let x = 1;
    2. match x {
    3. y => println!("x: {} y: {}", x, y),
    4. }

    这会打印出:

    1. x: 1 y: 1

    注意在同一匹配块中同时拥有捕获全部的_和捕获全部的绑定会产生错误:

    1. let x = 1;
    2. match x {
    3. y => println!("x: {} y: {}", x, y),
    4. _ => println!("anything"), // this causes an error as it is unreachable
    5. }

    这里有一个模式的陷阱:就像任何引入一个新绑定的语句,他们会引入隐藏。例如:

    1. let x = 1;
    2. let c = 'c';
    3. match c {
    4. x => println!("x: {} c: {}", x, c),
    5. }
    6. println!("x: {}", x)

    这会打印:

    1. x: c c: c
    2. x: 1

    换句话说,x =>匹配到了模式并引入了一个叫做x的新绑定。这个新绑定的作用域是匹配分支并拥有c的值。注意匹配作用域外的x的值对内部的x的值并无影响。因为我们已经有了一个x,新的x隐藏了它。

    多重模式(Multiple patterns)

    你可以使用|匹配多个模式:

    1. let x = 1;
    2. match x {
    3. 1 | 2 => println!("one or two"),
    4. 3 => println!("three"),
    5. _ => println!("anything"),
    6. }

    这会输出one or two

    解构(Destructuring)

    如果你有一个复合数据类型,例如一个结构体,你可以在模式中解构它:

    1. struct Point {
    2. x: i32,
    3. y: i32,
    4. }
    5. let origin = Point { x: 0, y: 0 };
    6. match origin {
    7. Point { x, y } => println!("({},{})", x, y),
    8. }

    我们可以用:来给出一个不同的名字:

    1. struct Point {
    2. x: i32,
    3. y: i32,
    4. }
    5. let origin = Point { x: 0, y: 0 };
    6. match origin {
    7. Point { x: x1, y: y1 } => println!("({},{})", x1, y1),
    8. }

    如果你只关心部分值,我们不需要给它们都命名:

    1. struct Point {
    2. x: i32,
    3. y: i32,
    4. }
    5. let point = Point { x: 2, y: 3 };
    6. match point {
    7. Point { x, .. } => println!("x is {}", x),
    8. }

    这会输出x is 2

    你可以对任何成员进行这样的匹配,不仅仅是第一个:

    1. struct Point {
    2. x: i32,
    3. y: i32,
    4. }
    5. let point = Point { x: 2, y: 3 };
    6. match point {
    7. Point { y, .. } => println!("y is {}", y),
    8. }

    这会输出y is 3

    这种“解构”行为可以用在任何复合数据类型上,例如元组和枚举

    忽略绑定(Ignoring bindings)

    你可以在模式中使用_来忽视它的类型和值。例如,这是一个Result<T, E>match

    1. # let some_value: Result<i32, &'static str> = Err("There was an error");
    2. match some_value {
    3. Ok(value) => println!("got a value: {}", value),
    4. Err(_) => println!("an error occurred"),
    5. }

    在第一个分支,我们绑定了Ok变量中的值为value,不过在Err分支,我们用_来忽视特定的错误,而只是打印了一个通用的错误信息。

    _在任何创建绑定的模式中都有效。这在忽略一个大大结构体的部分字段时很有用:

    1. fn coordinate() -> (i32, i32, i32) {
    2. // Generate and return some sort of triple tuple.
    3. # (1, 2, 3)
    4. }
    5. let (x, _, z) = coordinate();

    这里,我们绑定元组第一个和最后一个元素为xz,不过省略了中间的元素。

    值得注意的是,_ 一开始并不绑定值,这意味着值可能并没有被移动(这里涉及到 Move 和 Copy,应该就是说你不用它的话就不会 Move):

    1. let tuple: (u32, String) = (5, String::from("five"));
    2. // Here, tuple is moved, because the String moved:
    3. let (x, _s) = tuple;
    4. // The next line would give "error: use of partially moved value: `tuple`".
    5. // println!("Tuple is: {:?}", tuple);
    6. // However,
    7. let tuple = (5, String::from("five"));
    8. // Here, tuple is _not_ moved, as the String was never moved, and u32 is Copy:
    9. let (x, _) = tuple;
    10. // That means this works:
    11. println!("Tuple is: {:?}", tuple);

    这也意味着任何临时变量将会在语句结束时立刻被释放掉:

    1. // Here, the String created will be dropped immediately, as it’s not bound:
    2. let _ = String::from(" hello ").trim();

    你也可以在模式中用..来忽略多个值。

    1. enum OptionalTuple {
    2. Value(i32, i32, i32),
    3. Missing,
    4. }
    5. let x = OptionalTuple::Value(5, -2, 3);
    6. match x {
    7. OptionalTuple::Value(..) => println!("Got a tuple!"),
    8. OptionalTuple::Missing => println!("No such luck."),
    9. }

    这会打印Got a tuple!

    refref mut

    如果你想要一个引用,使用ref关键字:

    1. let x = 5;
    2. match x {
    3. ref r => println!("Got a reference to {}", r),
    4. }

    这会输出Got a reference to 5

    这里,match中的r&i32类型的。换句话说,ref关键字创建了一个在模式中使用的引用。如果你需要一个可变引用,ref mut同样可以做到:

    1. let mut x = 5;
    2. match x {
    3. ref mut mr => println!("Got a mutable reference to {}", mr),
    4. }

    范围(Ranges)

    你可以用...匹配一个范围的值:

    1. let x = 1;
    2. match x {
    3. 1 ... 5 => println!("one through five"),
    4. _ => println!("anything"),
    5. }

    这会输出one through five

    范围经常用在整数和char上。

    1. let x = '?';
    2. match x {
    3. 'a' ... 'j' => println!("early letter"),
    4. 'k' ... 'z' => println!("late letter"),
    5. _ => println!("something else"),
    6. }

    这会输出something else

    绑定

    你可以使用@把值绑定到名字上:

    1. let x = 1;
    2. match x {
    3. e @ 1 ... 5 => println!("got a range element {}", e),
    4. _ => println!("anything"),
    5. }

    这会输出got a range element 1。在你想对一个复杂数据结构进行部分匹配的时候,这个特性十分有用:

    1. #[derive(Debug)]
    2. struct Person {
    3. name: Option<String>,
    4. }
    5. let name = "Steve".to_string();
    6. let x: Option<Person> = Some(Person { name: Some(name) });
    7. match x {
    8. Some(Person { name: ref a @ Some(_), .. }) => println!("{:?}", a),
    9. _ => {}
    10. }

    这会输出 Some("Steve"),因为我们把Person里面的name绑定到a

    如果你在使用|的同时也使用了@,你需要确保名字在每个模式的每一部分都绑定名字:

    1. let x = 5;
    2. match x {
    3. e @ 1 ... 5 | e @ 8 ... 10 => println!("got a range element {}", e),
    4. _ => println!("anything"),
    5. }

    守卫(Guards)

    你可以用if来引入匹配守卫match guards):

    1. enum OptionalInt {
    2. Value(i32),
    3. Missing,
    4. }
    5. let x = OptionalInt::Value(5);
    6. match x {
    7. OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"),
    8. OptionalInt::Value(..) => println!("Got an int!"),
    9. OptionalInt::Missing => println!("No such luck."),
    10. }

    这会输出Got an int!

    如果你在if中使用多重模式,if条件将适用于所有模式:

    1. let x = 4;
    2. let y = false;
    3. match x {
    4. 4 | 5 if y => println!("yes"),
    5. _ => println!("no"),
    6. }

    这会打印no,因为if适用于整个4 | 5,而不仅仅是5,换句话说,if语句的优先级是这样的:

    1. (4 | 5) if y => ...

    而不是这样:

    1. 4 | (5 if y) => ...

    混合与匹配(Mix and Match)

    (口哨)!根据你的需求,你可以对上面的多种匹配方法进行组合:

    1. match x {
    2. Foo { x: Some(ref name), y: None } => ...
    3. }

    模式十分强大。好好使用它们。