枚举
Rust 的枚举是一个常用且实用的功能, 不同于其他语言的枚举
Rust 的 enum 也被称为标签联合(tagged-union)
rust
// Rust 的枚举和其他语言的枚举不同,
// 每个可枚举字段不仅仅是一个固定的值,
// 它可以包含更多数据, 更像一个特殊的 struct
#[derive(Debug)]
enum Message {
Quit, // 不关联任何数据
Move { // 类似结构体的数据, 包含命名字段
x: i32,
y: i32,
},
Write(String), // 包含一个字符串
ChangeColor(i32, i32, i32), // 包含多个i32的数据
}
impl Message {
// 可以给枚举定义方法
fn f1(&self) {
println!("f1 called");
}
}
fn main() {
let m1 = Message::Write(String::from("str"));
m1.f1();
let m2 = Message::Move { x: 1, y: 2 };
dbg!(m2);
}
match 模式匹配
Rust 的模式匹配非常像其他编程语言的 switch/case 语句
不同的是, match 语句必须要让值是穷尽的, 也就是说没有匹配到的情况也必须要加上
rust
fn main() {
let s = 11;
match s {
1 => println!("1"),
// 一次匹配多个值, 类似
// case 3:
// case 5:
// case 7:
// break;
3|5|7 => println!("3,5,7,9"),
// 匹配一个范围内的数字
10..20 => println!("10-20"),
_ => println!("unknown"),
}
}
rust
fn main() {
// 使用标准库的 Option 枚举
// let x: Option<i32> = None;
let x: Option<i32> = Some(1);
// let x: Option<i32> = Some(11);
// let x: Option<i32> = Some(2);
match x {
// case None 的情况
None => println!("x value is none"),
// case 1 的情况
Some(1) => {
println!("x = 1");
},
// case 11 的情况
Some(11) => {
println!("x = 11");
},
// _ 代表 switch-case 最后那个 default
// 当没有匹配到上面任何一个 case 的时候执行
_ => println!("not matched"),
}
}
rust
enum Season {
Spring,
Summer,
Autumn,
Winter,
}
fn main() {
let s = Season::Summer;
match s {
Season::Spring => println!("春"),
Season::Summer => println!("夏"),
Season::Autumn => println!("秋"),
Season::Winter => println!("冬"),
_ => println!("unknown"),
}
}
rust
fn main() {
let x = Some(5);
match x {
// 不能这样匹配:
// 第一个匹配项是 i32
// 第二个匹配项是 Option 枚举
1 => println!("matched: x = 1"),
Some(5) => println!("matched: x = 5"),
_ => println!("default, matched value is {:?}", x),
}
}
if let 简洁控制流
rust
enum Season {
Spring,
Summer,
Autumn,
Winter,
}
fn main() {
let x = 11;
// 普通值可以这样判断
if x == 11 {
println!("if: x=11");
}
// 如果是个枚举, 就无法直接 if 判断, 以为类型不一样
// 虽然可以用 match 来判断, 但是就一种情况, 感觉有些麻烦
let s = Some(11);
// match s {
// Some(11) => println!("match: x=11"),
// _ => (),
// }
// if let 的写法等同于上面的 match 写法
// 这个表达式中的 = 不是赋值的意思
// 注意必须 Some(xx) 在前, 要判断的值在后,
// 否则报错, 且判断有误, 就像这样, 也能进入 if
// if let s = Some(15) {
// println!("if-let:x=15");
// }
if let Some(12) = s {
println!("if-let:x=11");
} else {
println!("if-let: else");
}
// if let 也可以用于自定义枚举
// 还可以多分支形式的判断
let summer = Season::Summer;
if let Season::Spring = summer {
println!("Season::Spring");
} else if let Season::Summer = summer {
println!("Season::Summer");
} else {
println!("not Season::Summer");
}
}
match 和 if let 的区别
match 必须穷尽所有可能, if let 则不需要
- match 类似其他
C-Like
语言的 switch case 语句 - if let 则类似 if ...elseif...else 语句
rust
enum Season {
Spring,
Summer,
Autumn,
Winter,
}
fn main() {
let s = Season::Summer;
match s {
Season::Spring => println!("春"),
Season::Summer => println!("夏"),
// Season::Autumn => println!("秋"),
// Season::Winter => println!("冬"),
_ => println!("unknown"),
// _ 代表枚举其他任何可能的值,
// 1. 要么将所有的值列举出来, 来实现穷尽所有枚举可能的值
// 2. 要么设置一个默认的, 如果没有匹配到, 就使用这个默认的处理
// 3. 这个 match 等价于 =>
// if let Season::Spring = s {
// println!("春");
// } else if let Season::Summer = s {
// println!("夏");
// } else {
// println!("unknown");
// }
}
// 但是 if let 可以不穷尽枚举所有可能的值,
// 只匹配枚举的其中值的一个进行处理
if let Season::Summer = s {
println!("夏");
}
}
while let 简洁循环控制流
rust
fn main() {
let mut stk = Vec::new();
stk.push(1);
stk.push(3);
stk.push(5);
// 1. stk.pop 返回的是一个 Option 枚举
// 1.1 有值就是: Some(val)
// 1.2 没值就是: None
// 2. 只要 stk.pop 匹配的是 Some(val) 而不是 None,
// 这个循环的条件就会一直成立
while let Some(item) = stk.pop() {
println!("pop item is: {}", item);
}
}
模式
- let 模式:
let pattern = expression;
- for 模式:
for pattern in expression {}
- 函数参数:
fn f1((x, y): (u32, u32)) {}
rust
fn f1((x,y): (u32, u32)) {
println!("x:{}, y:{}", x, y);
}
fn main() {
// 类似 JS 的结构语法
let (x, y) = (1, 2);
println!("x: {} y:{}", x, y);
// 警告: unnecessary parentheses around pattern
// 翻译: 模式周围不闭包的括号, 因为编译器会将代码
// 识别为: let m = (3, 4);
let (m) = (3, 4);
// for 模式
let items = vec![1, 2, 3];
for (index, item) in items.iter().enumerate() {
println!("items[{}]: {}", index, item);
}
// 函数参数, 传入元组
f1((1, 2));
}
模式的两种形式
- 不可辩驳的: 能匹配任何可能传递的值得模式(赋值语句/函数参数)
- 可以辩驳的: 都某些可能的值, 无法进行匹配(if let/while let)
rust
fn main() {
// 这就是不可辩驳的, 无论右边的值是什么, 都能匹配上
let x = 1;
println!("{}", x);
// 这就是可辩驳的, 如果右边的值可能会是 None
// 是 None 的情况下就无法匹配成功
let o = Some(1);
// let o: Option<i32> = None;
if let Some(num) = o {
println!("{}", num);
}
// 不能这样赋值, 赋值语句和函数参数必须是不可辩驳的模式
// 编译器报错: error[E0005]: refutable pattern in local binding
// let x: Option<i32> = Some(2);
// let Some(value) = x;
// 不能这样来做判断, if let 和 while let 必须是可辩驳的模式
// 虽然能够通过编译, 但是编译器会报警告: irrefutable `if let` pattern
// 因为这样写是没有意义的, 永运都是可以匹配上的, 不可能会失败
if let x = 5 {
println!("x: {}", x);
}
}
模式匹配语法
直接匹配字面值
rust
fn main() {
let x = 2;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}
}
匹配命名变量
rust
fn main() {
let x = Some(5);
match x {
Some(1) => println!("matched: x = 1"),
// 此时的 x 只要是任意 Some(x) 的值
// 那么被 Some 包裹的值是什么, value 就是什么
Some(value) => println!("matched: x = {}", value),
// 编译器警告: unreachable pattern
// 注意: 匹配是从上倒下一项一项匹配
// 因为 Some(value) 肯定在前, 如果 Some(6) 可以匹配成功,
// 那么 Some(value) 这个匹配命名变量一定会匹配成功
// 也就是说, Some(6) 在匹配命名变量项后面, 不可能会执行到
Some(6) => println!("matched: x = 6"),
// 上面所有项都没有匹配成功的情况:
_ => println!("default, matched value is {:?}", x),
}
}
多种模式
也就是之前学的一次匹配多个值, 使用 |
语法
rust
fn main() {
let x = 5;
match x {
1|3|5|7 => println!("x is one of them: one, three, five, seven"),
2|4|6|8 => println!("x is one of them: two, four, six, eight"),
_ => println!("default, matched value is {:?}", x),
}
}
匹配范围
匹配一个值是否在一个范围中
rust
fn main() {
let x = 20;
match x {
// 1..=10: 表示 1-10 包括最后的10
1..=10 => println!("x is between 1 and 10"),
// 11..20: 表示 11-19 不包括最后的20
11..20 => println!("x is between 11 and 20"),
_ => println!("default, x value is {:?}", x),
}
// char 类型也可以使用范围匹配
let c = 'a';
match c {
'a'..='m' => println!("letter between in a-m"),
'n'..='z' => println!("letter between in n-z"),
_ => println!("ASCII letter not in a-z"),
}
}
解构分解值
可以使用模式来结构 struct
enum
tumple
从而引用这些类型的值得不同部分
rust
struct Position {
x: i32,
y: i32,
}
fn main() {
let p = Position { x: 1, y: 2 };
// 类似 JS 的解构赋值语法
let Position { x, y } = p;
println!("{} {}", x, y);
// 类似 JS 的解构赋值语法的重命名
let Position { x: px, y: py } = p;
println!("{} {}", px, py);
match p {
// y必须是0, x的值随意
Position { x, y: 0 } => println!("x: {}", x),
// x必须是0, y的值随意
Position { x: 0, y } => println!("y: {}", y),
// x 和 y 都可以随意
Position { x, y } => println!("x:{} and y:{}", x, y),
}
}
rust
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!("匹配不能存值的枚举项"),
Message::Move { x, y: 0 } => println!( "匹配类似结构体的枚举项,但y必须为0: {}", x),
Message::Move { x, y } => println!( "匹配类似结构体的枚举项: {}, {}", x, y),
Message::Write(text) => println!("匹配类似元组的枚举项:{}", text),
Message::ChangeColor(r, g, b) => println!("匹配类似元组的枚举项(多个值), {}, {}, {}", r, g, b)
}
}
rust
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 x = Message::ChangeColor(Color::Hsv(0, 160, 255));
match x {
Message::ChangeColor(Color::Rgb(r, g, b)) => {
println!( "r:{}, g:{}, b: {}", r, g, b)
},
Message::ChangeColor(Color::Hsv(h, s, v)) => {
println!( "h:{}, s:{}, v: {}", h, s, v)
}
_ => println!("Nothing to do"),
}
}
rust
struct Position {
x: i32,
y: i32,
}
fn main() {
// 解构需要一一对应
let ((element, is_in_map), Position{x, y}) = (("box", true), Position { x: 1, y: 2 });
println!("element is:{}", element);
println!("element is in map:{}", is_in_map);
println!("element position:x={}, y={}", x, y);
}
忽略模式中的值
rust
struct Position {
x: i32,
y: i32,
z: i32,
}
fn main() {
// _ 忽略未使用的变量/索引位置
let x = 1;
let _y = 2; // 忽略未使用的变量,否则会有警告, unused variable `y`
println!("x: {}", x);
let nums = (1, 3, 5, 7);
let (one, _, five, _) = nums; // 解构赋值时, 用 _ 忽略不需要的值
println!("one: {}, five: {}", one, five);
// .. 忽略字段/对应索引位置
let pos = Position { x: 1, y: 2, z: 3 };
match pos {
// x,y,z 字段的值必须都是0
Position { x:0, y:0, z:0 } => {
println!("Origin");
},
// x 的字段必须是0, 其他所有字段的值都忽略
Position { x: 0, .. } => {
println!("On x axis");
},
_ => println!("other"),
}
let nums = (1, 3, 5, 7);
match nums {
(1, .., 5) => {
println!("元组第一个元素必须是1, 最后一个元素必须是5");
},
(5, ..) => {
println!("元组第一个元素必须是5");
},
(.., 5) => {
println!("元组最后一个元素必须是5");
},
(first, .., last) => {
println!("元组最后一个元素和第一个元素可以是任何值, {}, {}", first, last);
},
}
}
使用 match 守卫来提供额外的条件
可以理解为其他语言的if语句有多个条件
rust
fn main() {
let x = Some(3);
match x {
// Some(v) 表示匹配任何 Some(x) 的值, 但是 v 必须满足 v < 5 这个条件
Some(v) if v < 5 => println!("less than five: {}", v),
// Some(v) 表示匹配任何 Some(x) 的值
Some(v) => println!("x:{}", v),
// None 的情况
None => println!("none"),
}
}
@ 绑定范围变量的值
rust
struct User {
id: i32,
}
enum Season {
Spring(u8),
Summer(u8),
Autumn(u8),
Winter(u8),
}
fn main() {
let x = 5;
// 绑定普通值
match x {
v @ 1..10 => println!("x value is {}", v),
_ => println!("x value is not between 1 and 10"),
}
// 绑定 struct 字段
let u = User { id: 6 };
match u {
User { id: x @ 1..10 } => println!("user id is {}", x),
_ => println!("user id is not between 1 and 10"),
}
// 绑定枚举
let s = Season::Spring(7);
match s {
Season::Spring(x @ 1..10) => println!("spring value is {}", x),
_ => println!("spring value is not between 1 and 10"),
}
}