当前位置:   article > 正文

《Rust权威指南》 第18章 模式匹配_rust权威指南第二版pdf

rust权威指南第二版pdf

一个模式通常由以下组件组成

  • 字面量
  • 结构的数组、枚举、结构体、元组
  • 变量
  • 通配符
  • 占位符

组件用于描述数据的形状,根据数据的形状进而对值进行匹配

所有可以使用模式的场合

match分支

match VALUE {
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
}
  • 1
  • 2
  • 3
  • 4
  • 5

match表达式必须穷尽匹配值的所有可能性

if let 表达式

可以混合使用 if let、else if 和 else if let 表达式,提供更大的灵活性

fn main() {
    let favorite_color: Option<&str> = None;
    let is_tuesday = false;
    let age: Result<u8, _> = "34".parse();

    if let Some(color) = favorite_color {
        println!("Using your favorite color, {}, as the background", color);
    } else if is_tuesday {
        println!("Tuesday is green day!");
    } else if let Ok(age) = age {
        if age > 30 {
            println!("Using purple as the background color");
        } else {
            println!("Using orange as the background color");
        }
    } else {
        println!("Using blue as the background color");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

while let 条件循环

反复执行同一个模式匹配直到匹配失败,自动退出循环

let mut stack = Vec::new();

stack.push(1);
stack.push(2);
stack.push(3);

while let Some(top) = stack.pop() {
    println!("{}", top);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

for 循环

for 模式 in 值(通常是一个迭代器的返回值)

例:迭代适配器enumerate会生成一个包含索引和值本身的元组

let v = vec!['a', 'b', 'c'];

for (index, value) in v.iter().enumerate() {
    println!("{} is at index {}", value, index);
}
  • 1
  • 2
  • 3
  • 4
  • 5

let 语句

实际上,所有的let语句都是模式匹配

let 模式 = 表达式;
  • 1

比如之前我们使用过的解构元组

let (x,y,z) = (1,2,3);
  • 1

函数的参数

传参过程是模式匹配(因为传参过程类似于一个赋值过程嘛)
比如通过模式匹配解构元组实参:

fn print_coordinates(&(x, y): &(i32, i32)) {
    println!("Current location: ({}, {})", x, y);
}

fn main() {
    let point = (3, 5);
    print_coordinates(&point);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Refutability :模式是否会匹配失败

  • 不可失败的模式能够匹配任何传入的值
    • 函数参数、let语句、for循环只能接受不可失败模式
  • 可失败模式则可能因为某些特定的值而匹配失败
    • if let 和 while let只能接受可失败模式

模式语法

匹配字面量

匹配时要求的是“全等”

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

匹配命名变量

匹配任何值,也就是类似于一般语言中为变量命名

fn main() {
    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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

多重模式

在模式中使用|来表示“或”

let x = 1;

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

匹配区间

使用a..=b表示匹配一个a到b的闭区间,比如1..=4就等同于1|2|3|4
范围模式只被允许使用数值或者char值

使用解构来分解值

解构结构体

使用语法

结构体名 {
	字段1:模式1,
	字段2:模式2} = 结构体实例
  • 1
  • 2
  • 3
  • 4

来直接获得结构体内部的值

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 因为变量名匹配字段名是常见的,同时因为 let Point { x: x, y: y } = p; 包含了很多重复,所以对于匹配结构体字段的模式存在简写:只需列出结构体字段的名称,则模式创建的变量会有相同的名称
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),
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

解构枚举

用于解构枚举的模式必须要对应枚举定义中存储数据的方式

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
            )
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

解构嵌套的结构体和枚举

模式可以嵌套使用,只要能表达清楚数据的结构就好

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
            )
        }
        _ => ()
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

解构结构体和元组

let ((feet,inches),Point {x,y}) = ((3,10),Point {x:-1,y:10});
  • 1

忽略模式中的值

使用_忽略整个值

比如为结构体实现trait时,trait的方法包含了不需要的参数,就用得上忽略模式了

fn foo(_: i32, y: i32) {
    println!("This code only uses the y parameter: {}", y);
}

fn main() {
    foo(3, 4);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

使用嵌套的_忽略值的某些部分

把被忽略的部分用_补上就好

fn main() {
let numbers = (2, 4, 8, 16, 32);

match numbers {
    (first, _, third, _, fifth) => {
        println!("Some numbers: {}, {}, {}", first, third, fifth)
    },
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

通过以_开头的名称来忽略未使用的变量

如果一个变量定义了却未被使用,会触发警告
使用_开头则可以忽略未使用的变量

  • _的区别:_不会执行变量绑定,而_变量名则会执行变量绑定
let s = Some(String::from("Hello!"));

if let Some(_s) = s {
    println!("found a string");
}
//报错!
//println!("{:?}", s);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
let s = Some(String::from("Hello!"));

if let Some(_) = s {
    println!("found a string");
}

println!("{:?}", s);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

使用..忽略值的剩余部分

..模式可以忽略一个值中没有被我们显式匹配的部分

  • ..会自动展开并填充任意多个值

    let numbers = (2, 4, 8, 16, 32);
    
    match numbers {
        (first, .., last) => {
            println!("Some numbers: {}, {}", first, last);
        },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 使用时不能出现歧义,比如下面这个例子就是有歧义

     let numbers = (2, 4, 8, 16, 32);
    
    match numbers {
        (.., second, ..) => {
    
    • 1
    • 2
    • 3
    • 4

使用匹配守卫添加额外条件

匹配守卫(match guard)是一个指定于 match 分支模式之后的额外 if 条件,它也必须被满足才能选择此分支。

let num = Some(4);

match num {
    Some(x) if x < 5 => println!("less than five: {}", x),
    Some(x) => println!("{}", x),
    None => (),
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

@绑定

运算符(@)允许我们在创建一个存放值的变量的同时测试其值是否匹配模式

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)
    },
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号