Rust入门笔记(三)

记录在https://course.rs 上学习Rust中的一些知识点

2.5.1 条件语句

  1. 基本语法

    1
    2
    3
    4
    5
    6
    7
    8
    if condition == true {
    // A...
    } else if {
    // B...
    } else {
    // C..
    }

  2. if语句块是个表达式,可以用来赋值。用来赋值的时候,多个分支返回的类型要一样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    fn main() {
    let condition = true;
    let number = if condition {
    5
    } else {
    6
    };

    println!("The value of number is: {}", number);
    }

2.5.2 for循环

  1. 基本语法

    1
    2
    3
    4
    5
    6
    7
    8
    for 元素 in 集合 {

    }

    // 例如
    for i in 1..=5 {
    println!("{}", i);
    }
  2. 如果被循环的集合没有实现copy特征,那么for语句中应该使用它的引用,不然for语句后它会因为所有权转移,没办法被继续使用

    1
    2
    3
    for item in &container {
    // ...
    }

    但如果集合是[i32:10]这种实现了copy特征的数组就无所谓

  3. 如果要修改集合中的元素,应该在集合前加mut关键字

  4. 使用方法 等价使用方式 所有权
    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() 可变借用
  5. 想省略元素的时候,可以用for _ in 0..10这种写法

  6. 对比一下两种写法
    第二种更优,因为编译器无需再运行时进行边界检查。而且第一种访问期间,collection可能发生变化。第二种因为所有权限制,不会变(已经被不可变借用了,就不能再被可变借用)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 第一种
    let collection = [1, 2, 3, 4, 5];
    for i in 0..collection.len() {
    let item = collection[i];
    // ...
    }

    // 第二种
    for item in collection {

    }
  7. continue和break语句和其他语言一样

2.5.3 while循环和loop循环

  1. while基本语法

    1
    2
    3
    4
    5
    while n <= 5  {
    println!("{}!", n);

    n = n + 1;
    }
  2. loop基本语法

    1
    2
    3
    loop {
    println!("again!");
    }
  3. loop一般配合break使用。loop本身是个表达式,可以通过break返回一个值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    fn main() {
    let mut counter = 0;

    let result = loop {
    counter += 1;

    if counter == 10 {
    break counter * 2;
    }
    };

    println!("The result is {}", result);
    }

2.6.1 模式匹配之match和if let

  1. 模式可以是字面值,可以包含一个待匹配的命名变量,比如模式Some(y)就可以匹配所有Some枚举的值,并且把值赋给变量y

  2. match匹配语法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    match target {
    模式1 => 表达式1,
    模式2 => {
    语句1;
    语句2;
    表达式2
    },
    _ => 表达式3
    }

    // 例如
    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"),
    };
    }

  3. match本身是个表达式,可以用来赋值给别人。值就是匹配到的模式对应的表达式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    fn main() {
    let ip1 = IpAddr::Ipv6;
    let ip_str = match ip1 {
    IpAddr::Ipv4 => "127.0.0.1",
    _ => "::1",
    };

    println!("{}", ip_str);
    }
  4. 利用模式匹配取到枚举里的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 一个枚举
    enum Action {
    ChangeColorRGB(u16, u16, u16),
    }
    Action::ChangeColorRGB(255,255,0)

    // 模式匹配
    match action {
    Action::ChangeColorRGB(r, g, _) => {
    println!("change color into '(r:{}, g:{}, b:0)', 'b' has been ignored",
    r, g,
    );
    }
    }

    // 最终输出
    change color into '(r:255, g:255, b:0)', 'b' has been ignored
  5. match的匹配必须穷尽所有可能性,比如匹配一个枚举,就要让枚举的所有可能值,都有对应的分支能进入,不能有漏网之鱼。可以用_来匹配未列举的所有模式,用其他变量名也可以匹配剩余场景,只是变量未使用会告警。

    1
    2
    3
    4
    5
    6
    7
    8
    let some_u8_value = 0u8;
    match some_u8_value {
    1 => println!("one"),
    3 => println!("three"),
    5 => println!("five"),
    7 => println!("seven"),
    _ => (),
    }
  6. if let匹配可以用来只匹配一个模式。当只关心一种场景,其余场景无所谓的时候,用if let更简洁。等号并非赋值,而是连接左侧模式和右侧表达式的符号。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 只关心v是Some(3)的场景
    if let Some(3) = v {
    println!("three");
    }

    // 用match就啰嗦了,因为必须穷尽所有场景
    let v = Some(3u8);
    match v {
    Some(3) => println!("three"),
    _ => (),
    }

  7. match和if let如果想要匹配其中的变量,最好不要用同名的,不然会有变量遮蔽的问题,增加理解成本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    fn main() {
    let age = Some(30);
    println!("在匹配前,age是{:?}",age); // Some(30)
    if let Some(age) = age {
    println!("匹配出来的age是{}",age);// 30
    }
    match age {
    Some(age) => println!("匹配出来的age是{}",age), // 30
    _ => ()
    }

    println!("在匹配后,age是{:?}",age); // Some(30)
    }
  8. matches!`可以将表达式和模式匹配,返回true或false

2.6.2 处理Option

前面枚举一节讲了特殊的枚举Option,用于处理可能值为空的场景

1
2
3
4
enum Option<T> {
None,
Some(T),
}

可以通过模式匹配,分别处理真的有值,和为None的场景,来避开空指针错误。使用例子如下

1
2
3
4
5
6
7
8
9
10
11
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}

let five = Some(5);
let six = plus_one(five); // Some(6)
let none = plus_one(None);

2.6.3 进一步理解模式匹配

本质上,解构赋值,和直接用let赋值,都是模式匹配

let赋值相当于let PATTERN = EXPRESSION;,然后将表达式里面被匹配到的值,绑定到模式里面的变量上

2.7 方法

区别于一般函数,方法可以理解为挂在对象上的一个函数。不过Rust里的方法一般是和结构体,枚举,特征一起使用。

通过impl关键字,来为结构体、枚举增加一个方法

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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)
}
}

fn main() {
let circle = Circle::new(0.0, 0.0, 2.0);
let a = circle.area();
println!("{}", a);
}

通过impl语法,来为Circle结构体增加了一个new方法,和area方法。方法的定义和对象的定义是分开的。对于同一个对象,可以有多个impl块。

其中,new的第一个参数不是self、&self、&mut self,它是一种关联函数,通常被当做构造函数来使用。一个对象可以有多个关联函数,比如一个new是依据传入参数来创建Circle对象的,再来一个不需要传参写死值的,都可以。名字也可以随便起,只要第一个参数不是self、&self、&mut self,就是关联函数。

area就是Circle的方法了,可以通过实例+.操作符来调用,&self不需要显式传参

  1. 方法的第一个参数:self、&self、&mut self,实际分别是self: Selfself: &Selfself: &mut Self的简写。三种参数对于是否转移所有权、是否可改原值有区别。直接使用self拿走所有权的场景很少
  2. 方法名字可以和属性名相同,如果不带括号,就是访问属性,带括号就是调用方法
  3. rust在调用方法的时候会自动引用和自动解引用,所以p1.distance(&p2);(&p1).distance(&p2);等价。一切以方法的函数签名里的类型为准,自动引用或解引用,直到和签名里的类型匹配上。
  4. 关联函数的调用不是通过.,而是::
  5. 为枚举添加方法和为结构体是一样的

Rust入门笔记(三)
https://miku03090831.github.io/2026/01/27/Rust入门笔记(三)/
作者
qh_meng
发布于
2026年1月27日
许可协议