文章

Rust基础——枚举与模式匹配

枚举

使用enum关键字声明一个枚举类型

1
2
3
4
5
6
7
enum IpAddress {
    V4,
    V6
}

let ipv4 = IpAddress::V4;
let ipv6 = IpAddress::V6;

枚举类型中的每个成员可以封装不同的数据

1
2
3
4
5
6
7
8
9
10
11
enum Message {
    Quit,
    Move { x: i64, y: i64 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

let quit = Message::Quit;  // 类单元结构体模式
let move1 = Message::Move { x: 1, y: 1 };  // 结构体模式
let write = Message::Write(String::from("hello"));  // 元组结构体模式
let change_color = Message::ChangeColor(1, 2, 3);  // 元组结构体模式

枚举类型可以使用impl块定义关联函数

1
2
3
4
5
impl Message {
    fn show(&self) {
        // ...
    }
}

Option枚举

rust使用Option枚举来实现null值的功能,rust中的非Option类型一定不为空,Option定义如下

1
2
3
4
enum Option<T> {
    None,  // rust中的null值对应到Option::None
    Some(T),
}

Option已经包含在preclude中,不需要显式引入作用域

1
2
3
let some_number = Some(5);
let some_string = Some(String::from("hello"));
let empty_number: Option<i32> = None;  // 赋值为None需要声明类型

Option::Some(T)类型与非Option类型不兼容,需要使用模式匹配来获取Some中的值

match匹配

match可以用于匹配模式,匹配必须是穷尽的

match匹配用于获取Option::Some(T)中的值

1
2
3
4
5
6
7
8
9
let some_number = Some(5);
match some_number {
    Some(n) => {
        // use 'n' variable
    }
    None => {
        // null action
    }
}

复杂模式匹配

match匹配可以匹配更加复杂的模式,主要可以匹配字面量、命名变量、数组、元组、结构体、元组结构体、类单元结构体、枚举,以及它们的嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enum Message {
    Quit,
    Move { x: i64, y: i64, z: i64 },
    Write(String),
    ChangeColor(i32, i32, i32),
    Numbers([i32; 3]),
}

let message = Message::Quit;
match quit {
    Message::Quit => {}  // 类单元结构体
    Message::Move { x: x1, y: 20, z } => {}  // 结构体+命名变量+字面量,使用结构体简化语法
    Message::Write(string) => {}  // 元组结构体+命名变量
    Message::ChangeColor(r, 10, b) => {}  // 元组结构体+命名变量+字面量
    Message::Numbers([a, b, c]) => {}  // 元组结构体+数组+命名变量
}

对于不需要匹配的分支,可以使用other分支进行默认行为

1
2
3
4
5
let n = 10;
match n {
    10 => {}
    other => {}  // 参数命名为other表示默认分支并获取值,可使用'_'占位符忽略值
}

match还支持序列匹配、单分支多模式和卫语句

1
2
3
4
5
6
7
8
9
10
11
12
13
let n = 10;
match n {
    10 | 20 => {}  // 对于单个分支匹配多个模式
    30..=50 => {}  // 匹配一个闭区间内的任何一个值,只能用于数值字面量和字符字面量
    other => {}
}

let num = Some(4);
match num {
    Some(x) if x < 5 => println!("less than five: {}", x),  // 注意卫语句匹配和一般匹配的顺序
    Some(x) => println!("{}", x),
    None => (),  // 无返回值
}

if let表达式

if let表达式可以用于简化Option匹配,实现只匹配Some分支而忽略None分支

1
2
3
4
5
6
7
8
9
10
11
12
13
let some_number = Some(5);
if let Some(n) = some_number {
    // use 'n' variable
}

// 也可以添加else if分支和else分支
if let Some(n) = some_number {
    // ...
} else if other_condition_or_matching {
    // ...
} else {
    // ...
}

模式匹配

rust中的模式匹配主要实现判断解构两个功能

许多类型都可以使用模式匹配进行解构,主要有以下类型

  • 命名变量:直接使用一个变量名进行匹配
  • 元组:使用(variable1, variable2)匹配
  • 数组:使用[variable1, variable2]匹配
  • 结构体:使用Structure { variable1, variable2 }匹配
  • 元组结构体:使用Tuple(variable1, variable2)匹配
  • 枚举:使用Enum::Member匹配

以上模式可以相互嵌套

模式匹配的六种情况

rust中以下六种情况可以使用模式匹配

  • match分支:实现判断解构功能

    1
    2
    3
    4
    
    match x {
        None => ()
        Some(n) => println!("{n}");
    }
    
  • if let表达式:实现判断解构功能

    1
    2
    3
    
    if let Some(n) => x {
        println!("{n}");
    }
    
  • while let条件循环:在匹配成功时执行循环,实现判断解构功能

    1
    2
    3
    
    while let Some(n) = x {
        println!("{n}");
    }
    
  • for循环:对遍历的元素使用模式匹配进行解构,实现解构功能

    1
    2
    3
    4
    
    let v = vec!['a', 'b', 'c'];
    for (index, value) in v.iter().enumerate() {
        println!("{value} is at index {index}");
    }
    
  • let语句:let声明变量可以看做一个模式匹配,实现解构功能

    1
    2
    3
    4
    5
    
    // let PATTERN = EXPRESSION;
    let a = 5;
    let (b, c) = (1, 2);
    let Point(x, y, z) = Point(1, 2, 3);
    let Rectangle { width, height } = Rectangle { width: 30, height: 50 };
    
  • 函数参数:在接收参数时可对参数进行模式匹配,实现解构功能

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

模式匹配的忽略变量

模式匹配中的命名变量可以使用_占位符忽略一个变量,使用..忽略多个变量

1
2
3
4
let arr = [1, 2, 3];
let [a, b, c] = arr;
let [a, b, _] = arr;  // 忽略一个变量
let [a, ..] = arr;  // 忽略剩余变量

模式的可反驳性(Refutability)

在rust中,模式有两种形式,分别是refutable(可反驳的)和irrefutable(不可反驳的)

  • irrefutable:模式可以匹配到任何可能值,永远不会失配,则模式称为不可反驳的
  • refutable:模式可能会失配,则称为可反驳的

在模式匹配的六种情况可以接受的模式不同

  • 函数参数、let语句和for循环只能使用不可反驳模式
  • if let表达式和while let循环可以使用两种模式
  • match一般分支必须使用可反驳模式,默认分支使用不可反驳模式,若match只有一个分支,则使用不可反驳模式

@绑定

@运算符可以在匹配字面量模式的同时获取字面量的值

1
2
3
4
5
6
7
8
9
10
11
12
13
enum Message {
    Hello { id: i32 },
}

let msg = Message::Hello { id: 5 };

match msg {
    // 匹配结构体字面量,同时创建一个变量接收字面量
    Message::Hello { id: id_variable @ 3..=7 } => {},
    // 匹配结构体字面量,但无法获取字面量
    Message::Hello { id: 10..=12 } => {}
    Message::Hello { id } => {},
}
本文由作者按照 CC BY 4.0 进行授权