RustPrimer
  • Introduction
  • 初识Rust
  • 安装Rust
    • Linux
    • Mac
    • Windows
    • 版本管理工具: rustup
  • 编辑器
    • 前期准备
    • vim
    • emacs
    • vscode
    • atom
    • sublime
    • visual studio
    • spacemacs
  • Rust快速入门
    • Rust旅程
    • 变量绑定与原生类型
    • 数组、动态数组和字符串
    • 结构体与枚举
    • 控制流
    • 函数与方法
    • 特性
    • 注释与文档
    • 输入输出流
  • Cargo项目管理器
  • 基本程序结构
    • 注释
    • 条件
    • 循环
  • 类型、运算符和字符串
    • 基础类型
    • 复合类型
    • 字符串类
    • 基础运算符和字符串格式化
  • 函数
    • 函数参数
    • 函数返回值
    • 语句和表达式
    • 高阶函数
  • 模式匹配
    • match关键字
    • 模式 pattern
  • 特征 Trait
    • trait关键字
    • trait对象
  • 泛型
  • 可变性、所有权、租借和生命期
    • 所有权
    • 引用和借用
    • 生命周期
  • 闭包
    • 闭包的语法
    • 闭包的实现
    • 闭包作为参数和返回值
  • 集合类型 Collections
    • 动态数组 Vec
    • 哈希表 HashMap
  • 迭代器
    • 迭代器、适配器、消费者
  • 模块和包系统、Prelude
    • 模块 module 和包 crate
    • Prelude
  • Option、Result与错误处理
  • 输入与输出
  • 宏系统
  • 堆、栈与Box
  • 几种智能指针
    • Rc, Arc
    • Mutex, RwLock
    • Cell, RefCell
  • 类型系统中的几个常见 Trait
    • Into/From 及其在 String 和 &str 互转上的应用
    • AsRef, AsMut
    • Borrow, BorrowMut, ToOwned
    • Deref 与 Deref coercions
    • Cow 及其在 String 和 &str 上的应用
  • Send 和 Sync
  • 并发,并行,多线程编程
    • 线程
    • 消息传递
    • 共享内存
    • 同步
    • 并行
  • Unsafe、原始指针
    • Unsafe
    • 原始指针
  • FFI
    • rust调用ffi函数
    • 将rust编译成库
  • 运算符重载
  • 属性和编译器参数
    • 属性
    • 编译器参数
  • Cargo参数配置
  • 测试与评测
    • 测试 (testing)
    • 评测 (benchmark)
  • 代码风格
  • Any与反射
  • 安全(safe)
  • 常用数据结构实现
    • 栈结构
    • 队列
    • 二叉树
    • 优先队列
    • 链表
    • 图结构
  • 标准库介绍
    • 系统命令:调用grep
    • 目录操作:简单grep
    • 网络模块:W回音
  • 实战篇
    • 实战:Json处理
    • 实战:Web 应用开发入门
    • 实战:使用Postgresql数据库
  • 附录-术语表
Powered by GitBook
On this page
  • 更强大的解构
  • 忽略和内存管理
  • 范围和多重匹配
  • 范围
  • 多重匹配
  • ref 和 ref mut
  • 变量绑定
  • 后置条件

Was this helpful?

  1. 模式匹配

模式 pattern

Previousmatch关键字Next特征 Trait

Last updated 5 years ago

Was this helpful?

模式,是Rust另一个强大的特性。它可以被用在let和match表达式里面。相信大家应该还记得我们在中提到的关于在let表达式中解构元组的例子,实际上这就是一个模式。

let tup = (0u8, 1u8);
let (x, y) = tup;

而且我们需要知道的是,如果一个模式中出现了和当前作用域中已存在的同名的绑定,那么它会覆盖掉外部的绑定。比如:

let x = 1;
let c = 'c';

match c {
    x => println!("x: {} c: {}", x, c),
}

println!("x: {}", x);

它的输出结果是:

x: c c: c
x: 1

在以上代码中,match作用域里的x这个绑定被覆盖成了'c',而出了这个作用域,绑定x又恢复为1。这和变量绑定的行为是一致的。

更强大的解构

在上一节里,我们初步了解了模式匹配在解构enum时候的便利性,事实上,在Rust中模式可以被用来对任何复合类型进行解构——struct/tuple/enum。现在我们要讲述一个复杂点的例子,对struct进行解构。

首先,我们可以对一个结构体进行标准的解构:

struct Point {
    x: i64,
    y: i64,
}
let point = Point { x: 0, y: 0 };
match point {
    Point { x, y } => println!("({},{})", x, y),
}

最终,我们拿到了Point内部的值。有人说了,那我想改个名字怎么办? 很简单,你可以使用 :来对一个struct的字段进行重命名,如下:

struct Point {
    x: i64,
    y: i64,
}
let point = Point { x: 0, y: 0 };
match point {
    Point { x: x1, y: y1} => println!("({},{})", x1, y1),
}

另外,有的时候我们其实只对某些字段感兴趣,就可以用..来省略其他字段。

struct Point {
    x: i64,
    y: i64,
}

let point = Point { x: 0, y: 0 };

match point {
    Point { y, .. } => println!("y is {}", y),
}

忽略和内存管理

总结一下,我们遇到了两种不同的模式忽略的情况——_和..。这里要注意,模式匹配中被忽略的字段是不会被move的,而且实现Copy的也会优先被Copy而不是被move。

说的有点拗口,上代码:

let tuple: (u32, String) = (5, String::from("five"));

let (x, s) = tuple;

// 以下行将导致编译错误,因为String类型并未实现Copy, 所以tuple被整体move掉了。
// println!("Tuple is: {:?}", tuple);

let tuple = (5, String::from("five"));

// 忽略String类型,而u32实现了Copy,则tuple不会被move
let (x, _) = tuple;

println!("Tuple is: {:?}", tuple);

范围和多重匹配

模式匹配可以被用来匹配单种可能,当然也就能被用来匹配多种情况:

范围

在模式匹配中,当我想要匹配一个数字(字符)范围的时候,我们可以用...来表示:

let x = 1;

match x {
    1 ... 10 => println!("一到十"),
    _ => println!("其它"),
}

let c = 'w';

match c {
    'a' ... 'z' => println!("小写字母"),
    'A' ... 'Z' => println!("大写字母"),
    _ => println!("其他字符"),
}

多重匹配

当我们只是单纯的想要匹配多种情况的时候,可以使用 | 来分隔多个匹配条件

let x = 1;

match x {
    1 | 2 => println!("一或二"),
    _ => println!("其他"),
}

ref 和 ref mut

前面我们了解到,当被模式匹配命中的时候,未实现Copy的类型会被默认的move掉,因此,原owner就不再持有其所有权。但是有些时候,我们只想要从中拿到一个变量的(可变)引用,而不想将其move出作用域,怎么做呢?答:用ref或者ref mut。

let mut x = 5;

match x {
    ref mut mr => println!("mut ref :{}", mr),
}
// 当然了……在let表达式里也能用
let ref mut mrx = x;

变量绑定

在模式匹配的过程内部,我们可以用@来绑定一个变量名,这在复杂的模式匹配中是再方便不过的,比如一个具名的范围匹配如下:

let x = 1u32;
match x {
    e @ 1 ... 5 | e @ 10 ... 15 => println!("get:{}", e),
    _ => (),
}

如代码所示,e绑定了x的值。

当然,变量绑定是一个极其有用的语法,下面是一个来自官方doc里的例子:

#[derive(Debug)]
struct Person {
    name: Option<String>,
}

let name = "Steve".to_string();
let x: Option<Person> = Some(Person { name: Some(name) });
match x {
    Some(Person { name: ref a @ Some(_), .. }) => println!("{:?}", a),
    _ => {}
}

输出:

Some("Steve")

后置条件

一个后置的if表达式可以被放在match的模式之后,被称为match guards。例如如下代码:

let x = 4;
let y = false;

match x {
    4 | 5 if y => println!("yes"),
    _ => println!("no"),
}

猜一下上面代码的输出?

答案是no。因为guard是后置条件,是整个匹配的后置条件:所以上面的式子可以被等同写为(4 | 5) if y。

复合类型