Rust-基础¶
1 变量与常量¶
- Rust 变量声明需要使用
let
关键字修饰 - 变量的类型可以指定,也可以不指定,类型在变量名之后,如:
let x: i32 = 2;
或者let x = 2;
- Rust 中变量默认是不可变的,如果需要可变,需要使用
mut
关键字修饰,如:let mut x = 5;
- 变量赋值时,可以指定类型,比如指定整形是
i32 还是 u64
,如let x = 2i32
- 变量赋值时,不指定类型,默认整形是
i32
,浮点型是f64
- Rust 中未使用的变量编译时会有警告,如果需要取消警告,可以将变量名以
_
开头,如let _x = 2;
, 获取使用属性修饰,如#![allow(dead_code)]
2 类型¶
2.1 基本类型¶
- unit 类型:
()
,这个就相对于什么数据都没有的类型。函数没有返回值时,其实就是返回了()
单元类型。 - char 类型:
char
- 字符串类型:
&str
,字符串字面值是不可变的,因为被硬编码到程序代码中 - bool 类型:
bool
- 浮点型:
f32、f64
- 整形:
长度 | 有符号类型 | 无符号类型 |
---|---|---|
8 位 | i8 |
u8 |
16 位 | i16 |
u16 |
32 位 | i32 |
u32 |
64 位 | i64 |
u64 |
128 位 | i128 |
u128 |
视架构而定 | isize |
usize |
- 示例
fn main() {
println!("hello world");
let c: char = 'h';
let b: bool = true;
let s: &str = "hello";
let str: String = String::from("hello");//动态字符串类型String
println!("c={} b={} s={} str={}", c, b, s, str);
let i: i32 = 2;
let f: f64 = 2.0;
println!("i={} f={}", i, f);
}
2.2 字符串(切片)¶
rust 中字符串有 2 种,一种是字面量字符串,类型是 &str
,另一种是 String
类型。
- 示例
#![allow(unused)]
fn main() {
let s: &str = "Hello, world!";
}
2.2.1 String¶
String
类型的底层是 [u8]
数组
2.3 数组¶
2.3.1 固定数组¶
title: rust数组与C++的不同?
1. rust数组定义语法如:`let a [i32:5] = [1, 2, 3, 4, 5];`,是使用`[]`来赋值,其中`[i32:5]`是`[类型:大小]`可以省略,而C++用`{}`。
2. rust数组不需要在数组名后加`[]`,如C++中是`char a[5];`
3. 数组访问2者是相同的,使用下标访问。
4. rust中函数或方法*参数一般不传数组*(因为不同大小的数组表示的是不同类型),而是传数组切片`&[T]`,而c++数组也相当于一个指针。
数组特点: - 存储在栈上 - 长度固定 - 元素必须有相同的类型 - 依次线性排列 语法示例:
fn main() {
let a1: [i32;5] = [1, 2, 3, 4, 5];
let a2 = [1, 2, 3, 4, 5];
// 3重复5次
let a3 = [3; 5];
let elm = a1[0];
println!("{:?}",a1);
println!("{:?}",a2);
println!("{:?}",a3);
println!("{}",elm);
}
- 可能输出
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[3, 3, 3, 3, 3]
1
2.4 动态数组 Vec¶
- 功能:大小可变的数组,存储在堆上。
- 动态数组 Vector
2.5 切片¶
rust中说的切片其实是**切片引用**,是对*数组的引用*或者*切片的引用*,签名是`&[Type]`,而数组签名是`[Type; Length]`。
字面量字符串其实是一种切片类型,叫做*字符串切片*,签名是`&str`,请注意字符串切片与其他类型切片的不同。
切片(Slice)跟数组相似,但是切片的长度无法在编译期得知,因此你无法直接使用切片类型;因此我们使用切片引用来代替切片,实际上切片也是只切片引用。
- 语法:切片使用序列(Range)语法来确定切片的范围
- let s = &val[start..end]
- let s = &val[start..=end]
- 示例
#![allow(unused)]
fn main() {
let s = String::from("hello");
let len = s.len();
// 字符串是切片,类型是&str
let slice1 : &str = &s[0..len];
// 对切片再次切片
let slice2 = &slice1[..2];
println!("{:?}",s);
println!("{:?}",slice1);
println!("{:?}",slice2);
let arr = [1, 2, 3];
// i32切片
let s1: &[i32] = &arr[0..=2];
println!("{:?}",s1);
}
- 可能的输出
"hello"
"hello"
"he"
[1, 2, 3]
2.6 元组¶
rust 中元组的签名是 (Type1,Type2,...,Typen)
元组中每一个元素可以是不同类型。
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
2.7 结构体¶
1. rust 中结构体和 C 结构体有点类似,有成员变量,不存在C++中的构造、析构函数。
2. rust 结构体也可以有方法(使用`impl`关键字声明),但是和C++类不同的是,rust*方法和数据是分离*的,不是在同一处定义。
3. 和C++一样,允许方法和字段(成员变量)同名。
rust 中结构体特点:
1. 和 C 一样使用 struct
关键字标识
2. 可以有多个成员变量
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
rust 结构体方法与其它语言的对比:
2.7.1 创建结构体¶
1. rust中初始化和C++一样,都可以使用花括号`{}`初始化
2. C++20开始支持指定初始化,但需要按成员变量声明顺序进行,而rust中不需要。
- 初始化实例时,每个字段都需要进行初始化
- 初始化时的字段顺序不需要和结构体定义时的顺序一致
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
2.7.2 使用结构体¶
rust和C++中一致,使用`.`来访问成员变量
#![allow(unused)]
fn main() {
// 默认变量是不可变的,因此想要修改变量,必须声明mut
let mut user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
}
2.7.3 更新结构体¶
结构体变量赋值,是**所有权的转让**,因此除了基本类型外的其他类型,在赋值结束后是不能再此使用的。但没有被**转移的字段依然可以使用**,比如下面示例中`user2.email`可以使用,但`user2.username`不能使用。
rust 中有需要很方便的设计来更新结构体成员变量的值。
当从一个结构体给另一个结构体赋值时,可以使用 ..变量名
的方式,但必须在结构体的尾部使用
fn main() {
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
let user2 = User {
active: user1.active,
username: user1.username,
email: String::from("another@example.com"),
sign_in_count: user1.sign_in_count,
};
let user3 = User{
email: String::from("hello@example.com"),
..user2 //必须在结构体尾部
};
println!("{:?}",user3);
}
2.7.4 元组结构体 (Tuple Struct)¶
结构体必须要有名称,但是结构体的字段可以没有名称,这种结构体长得很像元组,因此被称为元组结构体,例如:
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
元组结构体在你希望有一个整体名称,但是又不关心里面字段的名称时将非常有用。例如上面的 Point
元组结构体,众所周知 3D 点是 (x, y, z)
形式的坐标点,因此我们无需再为内部的字段逐一命名为:x
, y
, z
。
2.7.5 单元结构体 (Unit-like Struct)¶
单元结构体和单元类型很像,没有任何字段和属性,如果你定义一个类型,但是不关心该类型的内容, 只关心它的行为时,就可以使用单元结构体:
struct AlwaysEqual;
let subject = AlwaysEqual;
// 我们不关心 AlwaysEqual 的字段数据,只关心它的行为,因此将它声明为单元结构体,然后再为它实现某个特征
impl SomeTrait for AlwaysEqual {
}
2.8 枚举¶
rust中枚举很强大,不仅结合了C++中的限域枚举`enum class`风格,还结合了结构体风格,**每一个枚举值都可以有不同的类型**,甚至可以**存储值**。
2.8.1 无类型的枚举值¶
#[derive(Debug)]
enum PokerSuit {
Clubs,
Spades,
Diamonds,
Hearts,
}
fn main() {
let heart = PokerSuit::Hearts;
let diamond = PokerSuit::Diamonds;
println!("{:?}", heart);
println!("{:?}", diamond);
}
- 可能的输出
Hearts
Diamonds
2.8.2 有类型的枚举值¶
枚举值的类型可以是任意类型,指定类型时的方式却并不一样,如:
- 元组:`(T1,T2,..,Tn)`
- 匿名结构体:{v1:t1,..,vn:tn}
- 其它类型:`(T)`
- 示例
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let m1 = Message::Quit;
let m2 = Message::Move{x:1,y:1};
let m3 = Message::ChangeColor(255,255,0);
}
该枚举类型代表一条消息,它包含四个不同的成员:
- Quit
没有任何关联数据
- Move
包含一个匿名结构体
- Write
包含一个 String
字符串
- ChangeColor
包含三个 i32
其它示例:
#![allow(unused)]
#[derive(Debug)]
enum PokerCard {
Clubs(u8),
Spades(u8),
Diamonds(char),
Hearts(char),
}
fn main() {
let c1 = PokerCard::Spades(5);
let c2 = PokerCard::Diamonds('A');
println!("{:?}",c1);
println!("{:?}",c2);
}
- 可能的输出
Spades(5)
Diamonds('A')
3 所有权和引用¶
Rust通过所有权来管理内存。和 C++中手动管理内存的分配和释放不同。
3.1 所有权原则¶
- Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者
- 一个值同时只能被一个变量所拥有,或者说一个值只能拥有一个所有者
- 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop)
3.2 所有权转移¶
- 基本类型间赋值,默认是通过 copy 来赋值的,不会有所有权的转移。因此赋值后依然可以访问。
- 其他类型的变量赋值,默认是将所有权从 a 转移到 b,因此,赋值介绍后,a 就不能再访问了。如果通过深拷贝的方式来赋值,也不会有所有权的转移。
- 上述规则适用于给函数传参。
3.3 引用¶
引用机制可以让所有权转移实现的变量传递变得简洁
rust 引用类似于 C++中变量的指针和引用的结合体(虽然叫引用,但需要解引用,就形同 C++中解指针一样)。
- 用 &变量
引用,用 *变量
解引用
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
}
3.3.1 可变引用¶
rust 默认的引用时不可变的,即你无法修改引用的数据,相当于 C++中 const int * pi=2;
,不能修改 pi
的值。
3.3.2 不可变引用¶
要想使用不可变引用只需加上 mut
关键字即可。
可变引用的限制如下:
1. 只能有一个可变引用
2. 可变引用不能和不可变引用同时存在。
- 示例
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
3.3.3 悬垂引用 (Dangling References)¶
rust 悬垂引用和 C++悬垂指针一样,不能返回一个局部变量的引用。 - 示例
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s //出了函数,s被释放,变悬垂引用
}
4 流程控制¶
`if、for、while、loop`与C++语法上最大区别是:条件表达式不需要`()`包裹。
Rust 也有`break、continue`关键字,功能和C++完全一致,但break可以**带一个返回值**,有些类似 `return`。
4.1 if¶
Rust if 和C++中很像,都有`if、else、else if`这3种判断条件。但有2点不同:
1. Rust条件表达式不需要用`()`包裹。
2. Rust if语句是表达式,可以返回结果,但各分支返回值必须相同;而C++中不能。
- 示例
#![allow(unused)]
fn main() {
let number = 6;
let s = if number % 4 == 0 {
"number is divisible by 4"
} else if number % 3 == 0 {
"number is divisible by 3"
} else if number % 2 == 0 {
"number is divisible by 2"
} else {
"number is not divisible by 4, 3, or 2"
};
println!("{}",s);
}
- 可能的输出
number is divisible by 3
4.2 for¶
Rust 中 for 语法有 3 种格式,如下:
1. 循环集合,只关注值,如 for 元素 in 集合 {//表达式或语句}
2. 循环集合,同时关注索引、值,如:for (index, value) in 集合 {//表达式或语句}
3. 循环集合,忽略任何值、索引,如:for _ in 集合 {//表达式或语句}
- 示例
fn main() {
for i in 1..=5 {
println!("{}", i);
}
}
- 可能的输出
1
2
3
4
5
for 循环中也能使用可变 mut
、引用 &
语法,总结如下:
使用方法 | 等价使用方式 | 所有权 |
---|---|---|
for item in collection |
for item in IntoIterator::into_iter(collection) |
转移所有权 |
for item in &collection |
for item in collection.iter() |
不可变借用 |
for item in &mut collection |
for item in collection.iter_mut() |
可变借用 |
4.3 while¶
Rust中while和C++中while一样。
- 示例
fn main() {
let mut n = 0;
while n <= 5 {
println!("{}!", n);
n = n + 1;
}
println!("我出来了!");
}
4.4 loop¶
loop 有点类似与 while 1
,循环一直进行,除非循环体主动 break
或 return
。loop 还是一个表达式,因此可以返回一个值。
- 示例
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result);
}
- 可能的输出
The result is 20
4.5 break、continue¶
Rust中break、continue功能和C++只能一样,但break功能更强,**可以单独使用,也可以带一个返回值**,有些类似 `return`
5 模式匹配¶
5.1 可反驳的(refutable)和不可反驳的(irrefutable)¶
模式有两种形式:可反驳的(refutable)和不可反驳的(irrefutable)。
- 可反驳的(refutable):能匹配任何可能值的模式是不可反驳的。如 let x = 5;
中的 x,因为 x 可以匹配任何值所以不可能匹配失败。
- 不可反驳的(irrefutable):对于可能的值,存在匹配失败的情况的模式是可反驳的。如,表达式 if let Some (x) = a_value 中的 Some (x)
,如果变量 a_value 中的值是 None 而不是 Some,那么与模式 Some (x) 就会匹配失败。
5.2 match¶
1. rust中的match功能上和C++中的`switch case`一样,但语法格式完全不一样。
2. rust中match是一个表达式,因此match分支需要返回一个值。
match 语法格式如下:
match target {
模式1 => 表达式1,
模式2 => {
语句1;
语句2;
表达式2
},
_ => 表达式3
}
有以下几点值得注意:
1. match
的匹配必须要穷举出所有可能,因此这里用 _
来代表未列出的所有可能性
2. match
的每一个分支都必须是一个表达式,且所有分支的表达式最终返回值的类型必须相同
3. X | Y
,类似逻辑运算符 或
,代表该分支可以匹配 X
也可以匹配 Y
,只要满足一个即可。
4. 当 match
的是数值类型时还可以使用 1..=5
表示匹配数值 1~5
。
- 示例:请注意 println!
宏展开后其实也是个表达式。
enum Direction {
East,
West,
North,
South,
}
fn main() {
let dire = Direction::South;
match dire {
Direction::East => println!("East"),
Direction::North | Direction::South => {
println!("South or North");
},
_ => println!("West"),
};
}
5.2.1 模式绑定¶
在匹配有类型的枚举时,可以从枚举值中取出值。 - 示例
enum Action {
Say(String),
MoveTo(i32, i32),
ChangeColorRGB(u16, u16, u16),
}
fn main() {
let actions = [
Action::Say("Hello Rust".to_string()),
Action::MoveTo(1,2),
Action::ChangeColorRGB(255,255,0),
];
for action in actions {
match action {
Action::Say(s) => {
println!("{}", s);
},
Action::MoveTo(x, y) => {
println!("point from (0, 0) move to ({}, {})", x, y);
},
Action::ChangeColorRGB(r, g, _) => {
println!("change color into '(r:{}, g:{}, b:0)', 'b' has been ignored",
r, g,
);
}
}
}
}
5.3 if let¶
有时会遇到只有一个模式的值需要被处理,其它值直接忽略的场景,如果用 match
来处理就要写成下面这样:
let v = Some(3u8);
match v {
Some(3) => println!("three"),
_ => (),
}
使用 if let
如下:
#![allow(unused)]
fn main() {
let v = Some(3u8);
// if 也可以(注意后面的==号)
if Some(3) == v {
println!("three 1");
}
// if let 也可以,(注意后面的=号)
if let Some(3) = v {
println!("three 2");
}
}
从上面代码能看出,有些场景下使用 if let 和 if
作用相同,但有些场景 (比如变量覆盖) 就不行,如下
fn main() {
let age = Some(30);
println!("在匹配前,age是{:?}",age);
// 此代码不通过,需要使用`if let Some(age) = age`
/*if Some(age) == age {
println!("匹配出来的age是{}",age);
}*/
// if let 可以
if let Some(age) = age {
println!("匹配出来的age是{}",age);
}
println!("在匹配后,age是{:?}",age);
}
- 可能的输出
在匹配前,age是Some(30)
匹配出来的age是30
在匹配后,age是Some(30)
5.4 while let¶
和 if let
相似
- 示例
#![allow(unused)]
fn main() {
// Vec是动态数组
let mut stack = Vec::new();
// 向数组尾部插入元素
stack.push(1);
stack.push(2);
stack.push(3);
// stack.pop从数组尾部弹出元素
while let Some(top) = stack.pop() {
println!("{}", top);
}
}
- 可能的输出
3
2
1
6 解构¶
**解构也是通过模式匹配规则**来执行的。解构支持*结构体、枚举、元组、数组和引用*。
7 函数¶
rust中函数指的是**不和结构体关联**的函数。对应的是C++中非类成员函数。
rust中可以省略return关键字,默认将最后一个表达式的值返回。
- 函数结构:
fn func_name(param: type,...)->return_type{statment}
- 无返回值时,
->return_type
可以不写,或者是->()
- 和 C++一样,使用
return
关键字返回,也可以不使用。 - 和 C++一样,参数必须指定类型
- 参数类型和变量声明一样,也是放在名称后面
- rust 中函数可以放在任意位置,而 C++中要求使用的函数需要在当前位置之前出现,或者有函数声明。
- 无返回值时,
- 示例
fn sum(a: i64, b: i64) -> i64 {
return a + b;
}
fn main() {
println!("sum={}", sum(1, 2));
}
7.1 发散函数¶
发散函数是永远不会返回的函数,这个和没有返回值是不一样的。使用 !
标记返回类型
- 示例
fn foo() -> ! {
panic!("This call never returns.");
}
8 方法¶
1. rust中方法对应C++中的成员函数,也是和一个对象关联的。
2. rust中方法是通过关键字`impl`来定义的。
3. Rust 允许我们为一个结构体定义多个 `impl` 块
4. 方法中的`self`相当于C++中的`this`,表示当前对象。
- 示例
#![allow(unused)]
fn main() {
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
// new是Circle的关联函数,因为它的第一个参数不是self,且new并不是关键字
// 这种方法往往用于初始化当前结构体的实例
fn new(x: f64, y: f64, radius: f64) -> Circle {
Circle {
x: x,
y: y,
radius: radius,
}
}
// Circle的方法,&self表示借用当前的Circle结构体
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
}
8.1 self¶
`&self` 其实是 `self: &Self` 的简写(注意大小写)
self
关键字有 3 种形式:
1. self
:表示所有权转移到该方法中(这种形式用的较少,使用后导致调用方不再拥有所有权了,对象将释放)
2. &self
:表示该方法对此对象的不可变引用
3. &mut self
:表示该方法对此对象的可变引用
8.2 方法的调用¶
rust 种方法通过 对象.方法
的形式来使用,这种形式和 C++中相同;而 C++中还有指针通过 指针->方法
或者 (*指针).方法
的方式;rust 简化了这种场景,不管方法是 self
、&self
、&mut self
都只需要 对象.方法
形式访问。
- 示例
#![allow(unused)]
fn main() {
#[derive(Debug,Copy,Clone)]
struct Point {
x: f64,
y: f64,
}
impl Point {
fn distance(&self, other: &Point) -> f64 {
let x_squared = f64::powi(other.x - self.x, 2);
let y_squared = f64::powi(other.y - self.y, 2);
f64::sqrt(x_squared + y_squared)
}
}
let p1 = Point { x: 0.0, y: 0.0 };
let p2 = Point { x: 5.0, y: 6.5 };
// 以下2种调用时等价的
p1.distance(&p2);
(&p1).distance(&p2);
}
8.3 关联函数¶
rust中结构体关联函数和C++中static类成员函数相同,访问方式也时`结构体::方法`的形式。
这种定义在 impl
中且没有 self
的函数被称之为关联函数: 因为它没有 self
,不能用 f.read()
的形式调用,因此它是一个函数而不是方法,它又在 impl
中,与结构体紧密关联,因此称为关联函数。
在之前的代码中,我们已经多次使用过关联函数,例如 String::from
,用于创建一个动态字符串。
impl Rectangle {
fn new(w: u32, h: u32) -> Rectangle {
Rectangle { width: w, height: h }
}
}
Rust 中有一个约定俗成的规则,使用
new
来作为构造器的名称,出于设计上的考虑,Rust 特地没有用new
作为关键字
因为是函数,所以不能用 .
的方式来调用,我们需要用 ::
来调用,例如 let sq = Rectangle::new(3, 3);
。这个方法位于结构体的命名空间中:::
语法用于关联函数和模块创建的命名空间。
8.4 为枚举实现方法¶
枚举类型之所以强大,不仅仅在于它好用、可以同一化类型,还在于,我们可以像结构体一样,为枚举实现方法:
#![allow(unused)]
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
impl Message {
fn call(&self) {
// 在这里定义方法体
}
}
fn main() {
let m = Message::Write(String::from("hello"));
m.call();
}
9 格式化输出¶
9.1 格式化输出宏¶
`print!、println!、format!`其实是一个*表达式*。
print!
: 将格式化文本输出到标准输出,不带换行符println!
: 同上,但是在行的末尾添加换行符format!
: 将格式化文本输出到String
字符串 以上 3 个,又支持 3 种占位标志:{}
{:?}
:在结构体前声明#[derive(Debug)]
,就可以不自定义格式化器。{:#?}
10 宏¶
rust中的宏和C中的宏有很大不同,C中宏的定义有`#define`开始,可以有宏对象、宏函数。
在 Rust 中宏分为两大类:声明式宏( declarative macros) macro_rules!
和三种过程宏( procedural macros ):
- #[derive]
,在之前多次见到的派生宏,可以为目标结构体或枚举派生指定的代码,例如 Debug
特征
- 类属性宏(Attribute-like macro),用于为目标添加自定义的属性
- 类函数宏(Function-like macro),看上去就像是函数调用
11 增强功能¶
11.1 序列 (Range)¶
Range 用来生成连续的数值。
- 语法格式:
- num_start..num_end
:从 num_start 到 num_end-1 的连续数字,例如 1..5
,生成从 1 到 4 的连续数字
- num_start..=num_end
:从 num_start 到 num_end 的连续数字,1..=5
,生成从 1 到 5 的连续数字
- 示例
fn fun1(){
for i in 0..=5{
println!("{}",i);
}
}
fn main() {
fun1();
}
- 可能结果
0
1
2
3
4
5
11.2 NaN¶
- 功能:对于数学上未定义的结果,例如对负数取平方根
-42.1.sqrt()
,会产生一个特殊的结果:Rust 的浮点数类型使用NaN
(not a number)来处理这些情况。 - 判断数值是否合规:使用
is_nan()
函数 - 示例
fn main() {
let x = (-42.0_f32).sqrt();
if x.is_nan() {
println!("未定义的数学行为")
}
}
- 可能的输出
未定义的数学行为
11.3 通配符_¶
在 rust 中 _
符号是一个统配符,表示可以匹配任意内容,通常可以用在一下几处:
1. match
表达式结尾分支,当前面分支没有匹配时,_
就会匹配任意内容。
2. for in
循环中,当我们不关心值时,用 _
忽略值
3. _
甚至可以作为变量名前缀,表示未使用的变量,编译不会警告。
- 示例 1
for _ in 0..10 {
// ...
}
- 示例 2
enum Direction {
East,
West,
North,
South,
}
fn main() {
let dire = Direction::South;
match dire {
Direction::East => println!("East"),
Direction::North | Direction::South => {
println!("South or North");
},
_ => println!("West"),
};
}
11.4 忽略符..¶
..
可以出现在序列(Range)中,也可以用于忽略匹配值,忽略规则如下:
1. 忽略前值或后值
2. 忽略中间值
3. ..
必须是无歧义的
- 忽略前、后值:
#![allow(unused)]
fn main() {
struct Point {
x: i32,
y: i32,
z: i32,
}
let origin = Point { x: 0, y: 0, z: 0 };
match origin {
Point { x, .. } => println!("x is {}", x),
}
}
- 忽略中间值:
fn main() {
let numbers = (2, 4, 8, 16, 32);
match numbers {
(first, .., last) => {
println!("Some numbers: {}, {}", first, last);
},
}
}
- 有歧义的,编译失败:
fn main() {
let numbers = (2, 4, 8, 16, 32);
match numbers {
(.., second, ..) => {
println!("Some numbers: {}", second)
},
}
}