跳转至

Rust-基础

1 变量与常量

  1. Rust 变量声明需要使用 let 关键字修饰
  2. 变量的类型可以指定,也可以不指定,类型在变量名之后,如: let x: i32 = 2; 或者 let x = 2;
  3. Rust 中变量默认是不可变的,如果需要可变,需要使用 mut 关键字修饰,如:let mut x = 5;
  4. 变量赋值时,可以指定类型,比如指定整形是 i32 还是 u64,如 let x = 2i32
  5. 变量赋值时,不指定类型,默认整形是 i32,浮点型是 f64
  6. 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

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中不需要。
  1. 初始化实例时,每个字段都需要进行初始化
  2. 初始化时的字段顺序不需要和结构体定义时的顺序一致
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 所有权原则

  1. Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者
  2. 一个值同时只能被一个变量所拥有,或者说一个值只能拥有一个所有者
  3. 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop)

3.2 所有权转移

  1. 基本类型间赋值,默认是通过 copy 来赋值的,不会有所有权的转移。因此赋值后依然可以访问。
  2. 其他类型的变量赋值,默认是将所有权从 a 转移到 b,因此,赋值介绍后,a 就不能再访问了。如果通过深拷贝的方式来赋值,也不会有所有权的转移。
  3. 上述规则适用于给函数传参。

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,循环一直进行,除非循环体主动 breakreturnloop 还是一个表达式,因此可以返回一个值。 - 示例

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)
        },
    }
}