rust 从零单排 之 一战到底

Rust 语法详解 - 白话版

写给想学 Rust 的朋友,尽量用大白话把概念讲清楚,配合大量代码示例。


目录

  1. [为什么 Rust 这么难?](#为什么 Rust 这么难? "#%E4%B8%BA%E4%BB%80%E4%B9%88-rust-%E8%BF%99%E4%B9%88%E9%9A%BE")
  2. [安装与 Hello World](#安装与 Hello World "#%E5%AE%89%E8%A3%85%E4%B8%8E-hello-world")
  3. 变量与可变性
  4. 基本数据类型
  5. 函数
  6. 控制流
  7. [所有权 - Rust 的灵魂](#所有权 - Rust 的灵魂 "#%E6%89%80%E6%9C%89%E6%9D%83---rust-%E7%9A%84%E7%81%B5%E9%AD%82")
  8. 引用与借用
  9. 切片
  10. 结构体
  11. 枚举与模式匹配
  12. [Option 和 Result - 没有 Null 的世界](#Option 和 Result - 没有 Null 的世界 "#option-%E5%92%8C-result---%E6%B2%A1%E6%9C%89-null-%E7%9A%84%E4%B8%96%E7%95%8C")
  13. 错误处理
  14. 泛型
  15. [Trait - Rust 的接口](#Trait - Rust 的接口 "#trait---rust-%E7%9A%84%E6%8E%A5%E5%8F%A3")
  16. 生命周期
  17. 闭包
  18. 迭代器
  19. 模块系统
  20. 常用集合
  21. 智能指针
  22. 并发编程
  23. 常见陷阱与技巧

为什么 Rust 这么难?

Rust 难,主要难在这几点:

  1. 所有权系统 - 这是 Rust 独有的,其他语言没有
  2. 借用检查器 - 编译器会盯着你的引用,不让你乱来
  3. 生命周期 - 告诉编译器引用能活多久

但正是这些"难"的东西,让 Rust 写出来的程序没有空指针异常没有数据竞争内存安全


安装与 Hello World

安装

bash 复制代码
# Linux/macOS
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 验证安装
rustc --version
cargo --version

Hello World

rust 复制代码
// main.rs
fn main() {
    println!("Hello, World!");  // println! 是宏,不是函数,所以有感叹号
}
bash 复制代码
# 编译运行
rustc main.rs
./main

# 或者用 cargo(推荐)
cargo new hello_world
cd hello_world
cargo run

Cargo 常用命令

bash 复制代码
cargo new project_name    # 创建新项目
cargo build              # 编译(debug 模式)
cargo build --release    # 编译(release 模式,优化更多)
cargo run                # 编译并运行
cargo check              # 只检查错误,不生成二进制(快)
cargo test               # 运行测试
cargo doc --open         # 生成并打开文档

变量与可变性

默认不可变

Rust 的变量默认是不可变的,这是为了安全。

rust 复制代码
fn main() {
    let x = 5;
    println!("x = {}", x);
    
    // x = 6;  // 错误!不能修改不可变变量
}

用 mut 让变量可变

rust 复制代码
fn main() {
    let mut x = 5;  // 加上 mut 就可以改了
    println!("x = {}", x);
    
    x = 6;  // 没问题
    println!("x = {}", x);
}

变量遮蔽(Shadowing)

可以声明同名变量,新变量会遮蔽旧变量

rust 复制代码
fn main() {
    let x = 5;
    let x = x + 1;  // 遮蔽,x 现在是 6
    let x = x * 2;  // 再遮蔽,x 现在是 12
    
    println!("x = {}", x);  // 输出 12
    
    // 甚至可以改变类型!
    let spaces = "   ";      // 字符串
    let spaces = spaces.len(); // 变成数字,这在 mut 做不到
}

常量 vs 变量

rust 复制代码
// 常量:必须注明类型,必须在编译时就知道值
const MAX_POINTS: u32 = 100_000;  // 下划线增加可读性

fn main() {
    // 常量不能遮蔽,不能被修改,不能在运行时计算
    println!("Max points: {}", MAX_POINTS);
}

基本数据类型

整数类型

长度 有符号 无符号
8位 i8 u8
16位 i16 u16
32位 i32 u32
64位 i64 u64
128位 i128 u128
平台 isize usize
rust 复制代码
fn main() {
    let a: i32 = -100;      // 有符号 32 位,默认类型
    let b: u8 = 255;        // 无符号 8 位,0-255
    let c = 98_222;         // 下划线增加可读性
    let d = 0xff;           // 十六进制
    let e = 0o77;           // 八进制
    let f = 0b1111_0000;    // 二进制
    
    // 整数溢出(debug 模式会 panic,release 会回绕)
    // let overflow: u8 = 256;  // 错误!u8 最大是 255
}

浮点类型

rust 复制代码
fn main() {
    let x = 2.0;      // f64,默认类型,精度更高
    let y: f32 = 3.0; // f32
    
    // 浮点数运算
    let sum = x + 5.0;
    let diff = x - 1.0;
    let product = x * 3.0;
    let quotient = x / 2.0;
    let remainder = x % 2.0;
}

布尔类型

rust 复制代码
fn main() {
    let t = true;
    let f: bool = false;
    
    // 常用于条件判断
    if t {
        println!("是真的!");
    }
}

字符类型

rust 复制代码
fn main() {
    let c = 'z';
    let z = 'ℤ';
    let heart_eyed_cat = '😻';
    let chinese = '中';
    
    // char 是 Unicode 字符,占 4 字节
    println!("{} {} {} {}", c, z, heart_eyed_cat, chinese);
}

元组

rust 复制代码
fn main() {
    // 创建元组
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    
    // 解构
    let (x, y, z) = tup;
    println!("y = {}", y);
    
    // 索引访问
    let five_hundred = tup.0;
    let six_point_four = tup.1;
    let one = tup.2;
}

数组

rust 复制代码
fn main() {
    // 固定长度,栈上分配
    let arr = [1, 2, 3, 4, 5];
    
    // 指定类型和长度
    let arr2: [i32; 5] = [1, 2, 3, 4, 5];
    
    // 重复初始化:[3, 3, 3, 3, 3]
    let arr3 = [3; 5];
    
    // 访问元素
    let first = arr[0];
    let second = arr[1];
    
    // 越界会 panic!
    // let out_of_bounds = arr[100];  // 运行时错误
}

函数

基本语法

rust 复制代码
fn main() {
    say_hello();
    greet("世界");
    
    let sum = add(5, 3);
    println!("5 + 3 = {}", sum);
}

// 无返回值
fn say_hello() {
    println!("你好!");
}

// 有参数
fn greet(name: &str) {
    println!("你好,{}!", name);
}

// 有返回值(注意没有分号的最后一行)
fn add(a: i32, b: i32) -> i32 {
    a + b  // 这是返回值,没有分号!
    // 等价于: return a + b;
}

// 提前返回
fn abs(x: i32) -> i32 {
    if x < 0 {
        return -x;  // 提前返回,有分号
    }
    x  // 正常返回,无分号
}

语句 vs 表达式

这是 Rust 的一个重要概念:

rust 复制代码
fn main() {
    // 语句:执行动作,没有返回值
    let x = 5;  // 这是一个语句
    
    // 表达式:有返回值
    let y = {
        let x = 3;
        x + 1  // 表达式,没有分号,返回 4
    };
    
    println!("y = {}", y);  // y = 4
    
    // if 也是表达式!
    let condition = true;
    let number = if condition { 5 } else { 6 };
    println!("number = {}", number);
}

控制流

if 表达式

rust 复制代码
fn main() {
    let number = 7;
    
    // 基本 if
    if number < 5 {
        println!("小于 5");
    } else if number < 10 {
        println!("小于 10 但大于等于 5");
    } else {
        println!("大于等于 10");
    }
    
    // if 作为表达式(所有分支必须返回相同类型)
    let result = if number % 2 == 0 {
        "偶数"
    } else {
        "奇数"
    };
    println!("{} 是 {}", number, result);
}

loop 循环

rust 复制代码
fn main() {
    let mut count = 0;
    
    // 无限循环
    let result = loop {
        count += 1;
        
        if count == 10 {
            break count * 2;  // 可以返回值!
        }
    };
    
    println!("结果: {}", result);  // 20
    
    // 循环标签(嵌套循环时很有用)
    let mut remaining = 10;
    
    'outer: loop {
        println!("外层循环");
        
        loop {
            remaining -= 1;
            if remaining == 5 {
                break 'outer;  // 直接跳出外层循环
            }
        }
    }
    
    println!("结束");
}

while 循环

rust 复制代码
fn main() {
    let mut number = 3;
    
    while number != 0 {
        println!("倒计时: {}!", number);
        number -= 1;
    }
    
    println!("发射!");
    
    // 遍历数组
    let arr = [10, 20, 30, 40, 50];
    let mut index = 0;
    
    while index < 5 {
        println!("arr[{}] = {}", index, arr[index]);
        index += 1;
    }
}

for 循环

rust 复制代码
fn main() {
    // 遍历数组(推荐方式)
    let arr = [10, 20, 30, 40, 50];
    
    for element in arr {
        println!("元素: {}", element);
    }
    
    // 范围
    for number in 1..4 {  // 1, 2, 3(不含 4)
        println!("{}", number);
    }
    
    for number in 1..=4 {  // 1, 2, 3, 4(包含 4)
        println!("{}", number);
    }
    
    // 倒计时
    for number in (1..=5).rev() {
        println!("倒计时: {}", number);
    }
    println!("发射!");
}

match 匹配

rust 复制代码
fn main() {
    let number = 7;
    
    match number {
        1 => println!("一"),
        2 | 3 | 5 | 7 => println!("质数"),
        4..=10 => println!("4 到 10 之间"),
        _ => println!("其他数字"),  // _ 是通配符
    }
    
    // match 是表达式
    let result = match number {
        1 => "一",
        2 => "二",
        _ => "其他",
    };
    
    println!("结果: {}", result);
}

if let

rust 复制代码
fn main() {
    let some_value = Some(7);
    
    // 只关心一种情况时,比 match 简洁
    if let Some(7) = some_value {
        println!("是 7!");
    }
    
    // 等价于:
    match some_value {
        Some(7) => println!("是 7!"),
        _ => (),
    }
    
    // 可以加 else
    if let Some(x) = some_value {
        println!("值是 {}", x);
    } else {
        println!("没有值");
    }
}

所有权 - Rust 的灵魂

这是 Rust 最独特、最重要的概念。理解了所有权,就理解了 Rust 的一半。

三条规则

  1. Rust 中每个值都有一个所有者(owner)
  2. 同一时刻只能有一个所有者
  3. 当所有者离开作用域,值会被丢弃(drop)

变量作用域

rust 复制代码
fn main() {
    // s 不可用
    {
        let s = "hello";  // s 可用
        
        // 使用 s
        println!("{}", s);
    }  // s 离开作用域,被丢弃
    
    // s 不可用
}

String 类型

rust 复制代码
fn main() {
    // 字符串字面量:不可变,编译时就确定
    let s1 = "hello";  // &str 类型
    
    // String 类型:可变,堆上分配
    let mut s2 = String::from("hello");
    s2.push_str(", world!");  // 可以修改
    println!("{}", s2);
    
    // 为什么 String 可以修改而 &str 不行?
    // - &str 是编译时就确定的字符串切片,存在二进制文件中
    // - String 是运行时在堆上分配的,可以动态改变
}

移动(Move)

rust 复制代码
fn main() {
    let s1 = String::from("hello");
    let s2 = s1;  // s1 的所有权移动到 s2
    
    // println!("{}", s1);  // 错误!s1 已经无效了
    println!("{}", s2);     // 正确
    
    // 为什么?因为 String 包含堆上的数据
    // 如果只是复制指针,两个变量指向同一块内存
    // 离开作用域时会 double free!
    // Rust 选择移动所有权,让 s1 失效
}

克隆(Clone)

rust 复制代码
fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();  // 深拷贝
    
    println!("s1 = {}, s2 = {}", s1, s2);  // 两个都有效
    
    // clone 会复制堆上的数据,比较昂贵
    // 但有时你需要这样做
}

栈上的数据:复制

rust 复制代码
fn main() {
    let x = 5;
    let y = x;  // 复制,不是移动
    
    println!("x = {}, y = {}", x, y);  // 两个都有效
    
    // 为什么?因为整数存储在栈上,复制很便宜
    // 实现了 Copy trait 的类型会复制而不是移动
    // 包括:整数、浮点数、布尔值、字符、元组(如果元素都是 Copy)
}

函数与所有权

rust 复制代码
fn main() {
    let s = String::from("hello");
    takes_ownership(s);  // s 的所有权移动到函数
    
    // println!("{}", s);  // 错误!s 已经无效
    
    let x = 5;
    makes_copy(x);  // x 是 Copy 类型,所以复制
    
    println!("{}", x);  // x 仍然有效
}

fn takes_ownership(some_string: String) {
    println!("{}", some_string);
}  // some_string 离开作用域,被 drop

fn makes_copy(some_integer: i32) {
    println!("{}", some_integer);
}  // some_integer 离开作用域,但什么也不会发生(Copy)

返回值与所有权

rust 复制代码
fn main() {
    let s1 = gives_ownership();  // 函数把所有权转移给 s1
    
    let s2 = String::from("hello");
    let s3 = takes_and_gives_back(s2);  // s2 移动进函数,函数返回给 s3
    
    // println!("{}", s2);  // 错误!s2 已经无效
    println!("{}", s3);
}

fn gives_ownership() -> String {
    let some_string = String::from("hello");
    some_string  // 返回所有权给调用者
}

fn takes_and_gives_back(a_string: String) -> String {
    a_string  // 返回所有权
}

问题:太麻烦了!

rust 复制代码
fn main() {
    let s1 = String::from("hello");
    let (s2, len) = calculate_length(s1);  // 必须返回所有权...
    
    println!("'{}' 的长度是 {}", s2, len);
}

fn calculate_length(s: String) -> (String, usize) {
    let length = s.len();
    (s, length)  // 必须把 s 返回,否则调用者就用不了了
}

解决方案:引用!


引用与借用

什么是引用?

引用就像指针,但更安全。它让你借用值而不获取所有权。

rust 复制代码
fn main() {
    let s1 = String::from("hello");
    
    let len = calculate_length(&s1);  // 传递引用
    
    println!("'{}' 的长度是 {}", s1, len);  // s1 仍然有效!
}

fn calculate_length(s: &String) -> usize {  // 借用
    s.len()
}  // s 离开作用域,但不会 drop,因为它不拥有值

可变引用

rust 复制代码
fn main() {
    let mut s = String::from("hello");
    
    change(&mut s);
    
    println!("{}", s);  // hello, world
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

引用的规则

规则一:要么一个可变引用,要么多个不可变引用

rust 复制代码
fn main() {
    let mut s = String::from("hello");
    
    let r1 = &s;  // 不可变引用
    let r2 = &s;  // 不可变引用
    // let r3 = &mut s;  // 错误!不能同时有可变和不可变引用
    
    println!("{} {}", r1, r2);
    
    // 但是可以这样:
    let r3 = &mut s;  // 现在可以了,因为 r1, r2 已经不用了
    r3.push_str(" world");
}

规则二:引用必须始终有效(不能有悬垂引用)

rust 复制代码
fn main() {
    // let reference_to_nothing = dangle();  // 错误!
}

// 错误示例
fn dangle() -> &String {  // 返回 String 的引用
    let s = String::from("hello");
    
    &s  // 返回 s 的引用
}  // s 被 drop 了!引用指向了无效内存!

// 正确做法:返回所有权
fn no_dangle() -> String {
    let s = String::from("hello");
    s  // 返回 String,所有权转移
}

引用作用域

rust 复制代码
fn main() {
    let mut s = String::from("hello");
    
    let r1 = &s;
    let r2 = &s;
    println!("{} {}", r1, r2);
    // r1 和 r2 在这之后不再使用
    
    let r3 = &mut s;  // 现在可以了
    r3.push_str(" world");
}

切片

切片是对集合的一部分的引用。

字符串切片

rust 复制代码
fn main() {
    let s = String::from("hello world");
    
    let hello = &s[0..5];   // "hello"
    let world = &s[6..11];  // "world"
    
    println!("{} {}", hello, world);
    
    // 简写
    let hello = &s[..5];   // 从开头到索引 5
    let world = &s[6..];   // 从索引 6 到结尾
    let whole = &s[..];    // 整个字符串
    
    // 注意:字符串切片索引是按字节,不是字符!
    // 对于 UTF-8 字符串,要小心多字节字符
}

实际例子:找第一个单词

rust 复制代码
fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();
    
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    
    &s[..]
}

fn main() {
    let mut s = String::from("hello world");
    
    let word = first_word(&s);  // word 是 &str
    
    // s.clear();  // 错误!不能同时有可变和不可变引用
    
    println!("第一个单词: {}", word);
}

数组切片

rust 复制代码
fn main() {
    let a = [1, 2, 3, 4, 5];
    
    let slice = &a[1..3];  // [2, 3]
    
    println!("{:?}", slice);
}

结构体

定义和实例化

rust 复制代码
// 定义结构体
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

fn main() {
    // 创建实例
    let user1 = User {
        email: String::from("user@example.com"),
        username: String::from("user123"),
        active: true,
        sign_in_count: 1,
    };
    
    // 访问字段
    println!("用户名: {}", user1.username);
    
    // 可变实例
    let mut user2 = User {
        email: String::from("user2@example.com"),
        username: String::from("user456"),
        active: false,
        sign_in_count: 0,
    };
    
    user2.active = true;  // 修改字段
}

简写

rust 复制代码
fn build_user(email: String, username: String) -> User {
    User {
        email,    // 简写,变量名和字段名相同
        username,
        active: true,
        sign_in_count: 1,
    }
}

结构体更新语法

rust 复制代码
fn main() {
    let user1 = User {
        email: String::from("user1@example.com"),
        username: String::from("user1"),
        active: true,
        sign_in_count: 1,
    };
    
    // 从 user1 创建 user2,只修改部分字段
    let user2 = User {
        email: String::from("user2@example.com"),
        ..user1  // 其余字段从 user1 复制
    };
    
    // 注意:..user1 必须在最后
    // 注意:username 被 String::from 移动了,所以 user1.username 不能再使用
    // 但 active 和 sign_in_count 是 Copy 类型,所以 user1 还能用它们
}

元组结构体

rust 复制代码
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
    
    // 它们是不同类型,不能混用
    println!("黑色: ({}, {}, {})", black.0, black.1, black.2);
}

空结构体

rust 复制代码
struct AlwaysEqual;  // 没有任何字段

fn main() {
    let subject = AlwaysEqual;
}

打印结构体

rust 复制代码
#[derive(Debug)]  // 添加这个属性
struct User {
    username: String,
    email: String,
}

fn main() {
    let user = User {
        username: String::from("user"),
        email: String::from("user@example.com"),
    };
    
    println!("{:?}", user);   // 调试格式
    println!("{:#?}", user);  // 美化的调试格式
}

方法

rust 复制代码
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 关联函数(类似静态方法)
    fn new(width: u32, height: u32) -> Self {
        Self { width, height }
    }
    
    // 方法(第一个参数是 self)
    fn area(&self) -> u32 {
        self.width * self.height
    }
    
    fn width(&self) -> bool {
        self.width > 0
    }
    
    // 可变方法
    fn scale(&mut self, factor: u32) {
        self.width *= factor;
        self.height *= factor;
    }
    
    // 方法可以与字段同名
    // 通常用于实现 getter
}

fn main() {
    let rect = Rectangle::new(30, 50);  // 调用关联函数
    println!("rect: {:?}", rect);
    
    println!("面积: {}", rect.area());  // 调用方法
    println!("有宽度: {}", rect.width());
    
    let mut rect2 = Rectangle::new(10, 20);
    rect2.scale(2);
    println!("放大后: {:?}", rect2);
}

枚举与模式匹配

定义枚举

rust 复制代码
enum IpAddr {
    V4,
    V6,
}

fn main() {
    let four = IpAddr::V4;
    let six = IpAddr::V6;
    
    route(four);
    route(six);
}

fn route(ip_type: IpAddr) {
    match ip_type {
        IpAddr::V4 => println!("IPv4 地址"),
        IpAddr::V6 => println!("IPv6 地址"),
    }
}

枚举存储数据

rust 复制代码
enum IpAddr {
    V4(String),
    V6(String),
}

fn main() {
    let home = IpAddr::V4(String::from("127.0.0.1"));
    let loopback = IpAddr::V6(String::from("::1"));
    
    match home {
        IpAddr::V4(ip) => println!("IPv4: {}", ip),
        IpAddr::V6(ip) => println!("IPv6: {}", ip),
    }
}

更复杂的枚举

rust 复制代码
enum Message {
    Quit,                       // 没有关联数据
    Move { x: i32, y: i32 },    // 匿名结构体
    Write(String),              // 字符串
    ChangeColor(i32, i32, i32), // 三个整数
}

impl Message {
    fn call(&self) {
        match self {
            Message::Quit => println!("退出"),
            Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
            Message::Write(text) => println!("写入: {}", text),
            Message::ChangeColor(r, g, b) => println!("颜色: RGB({}, {}, {})", r, g, b),
        }
    }
}

fn main() {
    let m = Message::Write(String::from("hello"));
    m.call();
}

Option 枚举

Rust 没有 null,用 Option 表示可能不存在的值:

rust 复制代码
enum Option<T> {
    Some(T),
    None,
}

fn main() {
    let some_number = Some(5);
    let some_char = Some('e');
    let absent_number: Option<i32> = None;
    
    // 必须处理 None 的情况
    let x: Option<i32> = Some(5);
    let sum = match x {
        Some(n) => n + 1,
        None => 0,
    };
    
    println!("sum = {}", sum);
}

match 必须穷尽所有可能

rust 复制代码
fn main() {
    let number = 7;
    
    // match number {
    //     1 => println!("一"),
    //     2 => println!("二"),
    //     // 错误!没有处理其他情况
    // }
    
    // 正确:用 _ 处理其他情况
    match number {
        1 => println!("一"),
        2 => println!("二"),
        _ => println!("其他"),
    }
}

绑定值的模式

rust 复制代码
fn main() {
    let x = Some(5);
    let y = 10;
    
    match x {
        Some(50) => println!("是 50"),
        Some(y) => println!("匹配到 y = {}", y),  // 这里的 y 是新变量
        _ => println!("默认情况"),
    }
    
    println!("外部的 y = {}", y);  // y 还是 10
}

多重模式

rust 复制代码
fn main() {
    let x = 1;
    
    match x {
        1 | 2 => println!("一或二"),
        3..=5 => println!("三到五"),
        _ => println!("其他"),
    }
}

解构结构体和枚举

rust 复制代码
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 0, y: 7 };
    
    match p {
        Point { x: 0, y } => println!("在 y 轴上,y = {}", y),
        Point { x, y: 0 } => println!("在 x 轴上,x = {}", x),
        Point { x, y } => println!("在 ({}, {})", x, y),
    }
    
    // 解构时重命名
    match p {
        Point { x: a, y: b } => println!("({}, {})", a, b),
    }
    
    // 忽略某些字段
    match p {
        Point { x, .. } => println!("x = {}", x),
    }
}

匹配守卫

rust 复制代码
fn main() {
    let num = Some(4);
    
    match num {
        Some(x) if x < 5 => println!("小于 5: {}", x),
        Some(x) => println!("大于等于 5: {}", x),
        None => println!("没有值"),
    }
}

绑定变量@

rust 复制代码
fn main() {
    let age = 25;
    
    match age {
        n @ 0..=12 => println!("儿童: {}", n),
        n @ 13..=19 => println!("青少年: {}", n),
        n @ 20..=30 => println!("青年: {}", n),
        n => println!("其他年龄: {}", n),
    }
}

Option 和 Result - 没有 Null 的世界

Option:可能没有值

rust 复制代码
fn main() {
    let some = Some(5);
    let none: Option<i32> = None;
    
    // 方法 1:match
    let value = match some {
        Some(v) => v,
        None => 0,
    };
    
    // 方法 2:unwrap_or
    let value = some.unwrap_or(0);
    
    // 方法 3:unwrap(会 panic)
    // let value = some.unwrap();  // 如果是 None 会 panic
    
    // 方法 4:expect(自定义 panic 消息)
    // let value = some.expect("应该有值");
    
    // 方法 5:map
    let doubled = some.map(|x| x * 2);  // Some(10)
    
    // 方法 6:and_then
    let result = some.and_then(|x| {
        if x > 3 {
            Some(x * 2)
        } else {
            None
        }
    });
}

Result:可能出错

rust 复制代码
enum Result<T, E> {
    Ok(T),
    Err(E),
}

use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let f = File::open("hello.txt");
    
    let f = match f {
        Ok(file) => file,
        Err(error) => match error.kind() {
            ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("创建文件失败: {:?}", e),
            },
            other_error => panic!("打开文件失败: {:?}", other_error),
        },
    };
}

unwrap 和 expect

rust 复制代码
use std::fs::File;

fn main() {
    // unwrap:成功返回值,失败 panic
    let f = File::open("hello.txt").unwrap();
    
    // expect:可以自定义错误消息
    let f = File::open("hello.txt").expect("无法打开文件");
}

? 运算符

rust 复制代码
use std::fs::File;
use std::io;
use std::io::Read;

// ? 会自动处理错误,把 Err 返回给调用者
fn read_username_from_file() -> Result<String, io::Error> {
    let mut f = File::open("hello.txt")?;  // 如果失败,返回 Err
    let mut s = String::new();
    f.read_to_string(&mut s)?;  // 如果失败,返回 Err
    Ok(s)
}

// 更简洁的写法
fn read_username_from_file2() -> Result<String, io::Error> {
    let mut s = String::new();
    File::open("hello.txt")?.read_to_string(&mut s)?;
    Ok(s)
}

// 最简洁:使用标准库函数
fn read_username_from_file3() -> Result<String, io::Error> {
    std::fs::read_to_string("hello.txt")
}

? 也可以用于 Option

rust 复制代码
fn add_last(stack: &Vec<i32>) -> Option<i32> {
    let last = stack.last()?;  // 如果是 None,返回 None
    let second_last = stack.get(stack.len() - 2)?;  // 同上
    Some(last + second_last)
}

错误处理

不可恢复错误:panic!

rust 复制代码
fn main() {
    // panic!("崩溃了!");
    
    // 数组越界也会 panic
    let v = vec![1, 2, 3];
    // v[99];  // panic!
}

可恢复错误:Result

rust 复制代码
use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let greeting_file_result = File::open("hello.txt");
    
    let greeting_file = match greeting_file_result {
        Ok(file) => file,
        Err(error) => match error.kind() {
            ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("创建文件失败: {:?}", e),
            },
            other_error => {
                panic!("打开文件失败: {:?}", other_error);
            }
        },
    };
}

使用闭包简化

rust 复制代码
use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
        if error.kind() == ErrorKind::NotFound {
            File::create("hello.txt").unwrap_or_else(|error| {
                panic!("创建文件失败: {:?}", error);
            })
        } else {
            panic!("打开文件失败: {:?}", error);
        }
    });
}

泛型

泛型函数

rust 复制代码
// 泛型函数:可以处理多种类型
fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    
    for item in list {
        if item > largest {
            largest = item;
        }
    }
    
    largest
}

fn main() {
    let numbers = vec![34, 50, 12, 100, 65];
    let result = largest(&numbers);
    println!("最大的数字: {}", result);
    
    let chars = vec!['y', 'm', 'a', 'q'];
    let result = largest(&chars);
    println!("最大的字符: {}", result);
}

泛型结构体

rust 复制代码
// 泛型结构体
struct Point<T> {
    x: T,
    y: T,
}

// 多个泛型参数
struct Point2<T, U> {
    x: T,
    y: U,
}

fn main() {
    let integer = Point { x: 5, y: 10 };
    let float = Point { x: 1.0, y: 4.0 };
    // let mixed = Point { x: 5, y: 4.0 };  // 错误!T 必须是同一类型
    
    let mixed = Point2 { x: 5, y: 4.0 };  // 正确!
}

泛型方法

rust 复制代码
struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

// 为特定类型实现方法
impl Point<f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

fn main() {
    let p = Point { x: 5, y: 10 };
    println!("x = {}", p.x());
    
    let p_float = Point { x: 3.0, y: 4.0 };
    println!("距离原点: {}", p_float.distance_from_origin());
}

泛型枚举

rust 复制代码
// 标准库中的 Option 和 Result 就是泛型枚举
enum Option<T> {
    Some(T),
    None,
}

enum Result<T, E> {
    Ok(T),
    Err(E),
}

// 自定义泛型枚举
enum Container<T> {
    Single(T),
    Pair(T, T),
    Triple(T, T, T),
}

fn main() {
    let single = Container::Single(1);
    let pair = Container::Pair(1, 2);
    let triple = Container::Triple(1, 2, 3);
}

Trait - Rust 的接口

Trait 类似其他语言的接口,但更强大。

定义和实现 Trait

rust 复制代码
// 定义 Trait
pub trait Summary {
    fn summarize(&self) -> String;
}

// 为类型实现 Trait
pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }
}

pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}

impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

fn main() {
    let article = NewsArticle {
        headline: String::from("Rust 发布新版本"),
        location: String::from("北京"),
        author: String::from("张三"),
        content: String::from("Rust 1.70 发布了..."),
    };
    
    println!("{}", article.summarize());
    
    let tweet = Tweet {
        username: String::from("rustlover"),
        content: String::from("Rust 太棒了!"),
        reply: false,
        retweet: false,
    };
    
    println!("{}", tweet.summarize());
}

默认实现

rust 复制代码
pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(阅读更多...)")
    }
    
    // 默认实现可以调用其他方法
    fn summarize_author(&self) -> String;
    
    fn summarize_with_author(&self) -> String {
        format!("作者: {}。{}", self.summarize_author(), self.summarize())
    }
}

pub struct NewsArticle {
    pub headline: String,
    pub author: String,
}

impl Summary for NewsArticle {
    fn summarize_author(&self) -> String {
        self.author.clone()
    }
    // 不实现 summarize,使用默认实现
}

fn main() {
    let article = NewsArticle {
        headline: String::from("新闻标题"),
        author: String::from("张三"),
    };
    
    println!("{}", article.summarize());           // 默认实现
    println!("{}", article.summarize_with_author()); // 调用默认方法
}

Trait 作为参数

rust 复制代码
pub trait Summary {
    fn summarize(&self) -> String;
}

// 方式 1:impl Trait 语法
pub fn notify(item: &impl Summary) {
    println!("突发新闻!{}", item.summarize());
}

// 方式 2:Trait Bound 语法(更灵活)
pub fn notify2<T: Summary>(item: &T) {
    println!("突发新闻!{}", item.summarize());
}

// 多个 Trait
pub fn notify3(item: &(impl Summary + std::fmt::Display)) {
    println!("{}", item);
}

// 使用 where 子句(更清晰)
pub fn some_function<T, U>(t: &T, u: &U) -> i32
where
    T: std::fmt::Display + Clone,
    U: std::fmt::Debug + Clone,
{
    // 函数体
    0
}

返回 Trait

rust 复制代码
pub trait Summary {
    fn summarize(&self) -> String;
}

pub struct NewsArticle {
    pub headline: String,
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        self.headline.clone()
    }
}

// 返回实现了 Trait 的类型
fn returns_summarizable() -> impl Summary {
    NewsArticle {
        headline: String::from("新闻标题"),
    }
}

// 注意:不能返回不同类型!
// fn returns_summarizable(switch: bool) -> impl Summary {
//     if switch {
//         NewsArticle { headline: String::from("新闻") }
//     } else {
//         Tweet { username: String::from("user"), content: String::from("内容") }
//     }
// }
// 这会编译错误!

Trait Bound 条件

rust 复制代码
use std::fmt::Display;

// 只有实现了 PartialOrd 和 Copy 的类型才能用这个函数
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
    let mut largest = list[0];
    
    for &item in list {
        if item > largest {
            largest = item;
        }
    }
    
    largest
}

// 使用 where 让代码更清晰
fn some_function<T, U>(t: &T, u: &U) -> i32
where
    T: Display + Clone,
    U: Clone + std::fmt::Debug,
{
    0
}

常见 Trait

rust 复制代码
// Debug:打印调试信息
#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

// Clone:深拷贝
#[derive(Clone)]
struct Data {
    value: i32,
}

// Copy:栈上复制(隐式)
#[derive(Copy, Clone)]
struct Size {
    width: u32,
    height: u32,
}

// PartialEq:相等比较
#[derive(PartialEq)]
struct Person {
    name: String,
    age: u32,
}

fn main() {
    let p1 = Point { x: 1, y: 2 };
    println!("{:?}", p1);  // Debug
    
    let d1 = Data { value: 10 };
    let d2 = d1.clone();  // Clone
    
    let s1 = Size { width: 10, height: 20 };
    let s2 = s1;  // Copy(不需要 clone)
    
    let p1 = Person { name: String::from("张三"), age: 25 };
    let p2 = Person { name: String::from("张三"), age: 25 };
    println!("{}", p1 == p2);  // true
}

生命周期

生命周期是 Rust 最难的概念之一。它确保引用始终有效。

为什么需要生命周期?

rust 复制代码
// 这个函数会报错!
fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
// 编译器不知道返回的引用是 x 还是 y
// 所以不知道返回值的生命周期

生命周期标注

rust 复制代码
// 告诉编译器:返回值的生命周期与参数相同
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let string1 = String::from("long string");
    let string2 = String::from("xyz");
    
    let result = longest(&string1, &string2);
    println!("最长的字符串: {}", result);
    
    // 这个例子会报错
    let string3 = String::from("long string");
    let result;
    {
        let string4 = String::from("xyz");
        result = longest(&string3, &string4);  // string4 活得不够长
    }
    // println!("{}", result);  // 错误!string4 已经被释放
}

生命周期标注语法

rust 复制代码
// 生命周期参数以 ' 开头,通常用 'a
fn function<'a>(x: &'a str) -> &'a str {
    x
}

// 多个生命周期参数
fn function2<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
    x  // 返回 x,所以生命周期是 'a
}

// 结构体中的生命周期
struct ImportantExcerpt<'a> {
    part: &'a str,  // 引用必须比结构体活得长
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().unwrap();
    
    let excerpt = ImportantExcerpt {
        part: first_sentence,
    };
    
    // excerpt 离开作用域后,novel 仍然有效
}

生命周期省略

有些情况下,编译器可以自动推断生命周期:

rust 复制代码
// 省略前
fn first_word<'a>(s: &'a str) -> &'a str {
    let bytes = s.as_bytes();
    
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    
    &s[..]
}

// 省略后(编译器自动推断)
fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    
    &s[..]
}

静态生命周期

rust 复制代码
// 'static:整个程序运行期间都有效
let s: &'static str = "这是一个静态字符串";
// 字符串字面量都是 'static 的

// 但不要滥用 'static 来解决生命周期问题

方法的生命周期

rust 复制代码
struct ImportantExcerpt<'a> {
    part: &'a str,
}

impl<'a> ImportantExcerpt<'a> {
    fn level(&self) -> i32 {
        3
    }
    
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("请注意: {}", announcement);
        self.part
    }
}

闭包

闭包是可以捕获环境的匿名函数。

基本语法

rust 复制代码
fn main() {
    // 基本闭包
    let add = |a, b| a + b;
    println!("1 + 2 = {}", add(1, 2));
    
    // 带类型标注
    let add_explicit = |a: i32, b: i32| -> i32 {
        a + b
    };
    
    // 多行闭包
    let calculate = |a, b| {
        let sum = a + b;
        let product = a * b;
        (sum, product)
    };
    
    let (s, p) = calculate(3, 4);
    println!("和: {}, 积: {}", s, p);
}

捕获环境

rust 复制代码
fn main() {
    let x = 4;
    
    // 闭包可以捕获外部变量
    let equal_to_x = |z| z == x;
    
    let y = 4;
    assert!(equal_to_x(y));
    
    // 函数不能这样做!
    // fn equal_to_x(z: i32) -> bool {
    //     z == x  // 错误!函数不能捕获环境
    // }
}

闭包的三种捕获方式

rust 复制代码
fn main() {
    // 1. FnOnce:获取所有权(只能调用一次)
    let name = String::from("张三");
    let consume = move || {
        println!("再见,{}!", name);  // name 被移动到闭包中
    };
    consume();
    // println!("{}", name);  // 错误!name 已经被移动
    
    // 2. FnMut:可变借用
    let mut list = vec![1, 2, 3];
    let mut push_to_list = || {
        list.push(4);  // 可变借用
    };
    push_to_list();
    println!("{:?}", list);
    
    // 3. Fn:不可变借用
    let list = vec![1, 2, 3];
    let print_list = || {
        println!("{:?}", list);  // 不可变借用
    };
    print_list();
    println!("{:?}", list);  // list 仍然可用
}

闭包作为参数

rust 复制代码
fn main() {
    let list = vec![1, 2, 3, 4, 5];
    
    // 使用闭包
    let result: Vec<i32> = list.iter().map(|x| x * 2).collect();
    println!("{:?}", result);
    
    // 使用函数
    fn double(x: &i32) -> i32 {
        *x * 2
    }
    let result2: Vec<i32> = list.iter().map(double).collect();
    println!("{:?}", result2);
}

迭代器适配器

rust 复制代码
fn main() {
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    
    // map:转换
    let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
    println!("翻倍: {:?}", doubled);
    
    // filter:过滤
    let evens: Vec<&i32> = numbers.iter().filter(|x| *x % 2 == 0).collect();
    println!("偶数: {:?}", evens);
    
    // fold:累积
    let sum: i32 = numbers.iter().fold(0, |acc, x| acc + x);
    println!("总和: {}", sum);
    
    // 链式调用
    let result: Vec<i32> = numbers
        .iter()
        .filter(|x| *x % 2 == 0)  // 过滤偶数
        .map(|x| x * 2)            // 翻倍
        .take(3)                   // 取前 3 个
        .collect();
    println!("结果: {:?}", result);
}

迭代器

创建迭代器

rust 复制代码
fn main() {
    let v = vec![1, 2, 3];
    
    // iter():不可变引用
    let iter1 = v.iter();
    
    // iter_mut():可变引用
    let mut v2 = vec![1, 2, 3];
    let iter2 = v2.iter_mut();
    
    // into_iter():获取所有权
    let iter3 = v.into_iter();
}

Iterator trait

rust 复制代码
pub trait Iterator {
    type Item;
    
    fn next(&mut self) -> Option<Self::Item>;
    
    // 其他方法都有默认实现
}

fn main() {
    let v = vec![1, 2, 3];
    
    let mut iter = v.iter();
    
    assert_eq!(iter.next(), Some(&1));
    assert_eq!(iter.next(), Some(&2));
    assert_eq!(iter.next(), Some(&3));
    assert_eq!(iter.next(), None);
}

消费适配器

rust 复制代码
fn main() {
    let v = vec![1, 2, 3, 4, 5];
    
    // sum:求和
    let sum: i32 = v.iter().sum();
    println!("总和: {}", sum);
    
    // count:计数
    let count = v.iter().count();
    println!("数量: {}", count);
    
    // any:是否有任意元素满足条件
    let has_even = v.iter().any(|x| x % 2 == 0);
    println!("有偶数: {}", has_even);
    
    // all:是否所有元素都满足条件
    let all_positive = v.iter().all(|x| *x > 0);
    println!("都是正数: {}", all_positive);
    
    // find:查找第一个满足条件的元素
    let first_even = v.iter().find(|x| *x % 2 == 0);
    println!("第一个偶数: {:?}", first_even);
    
    // position:查找位置
    let pos = v.iter().position(|x| *x == 3);
    println!("3 的位置: {:?}", pos);
}

自定义迭代器

rust 复制代码
struct Counter {
    count: usize,
    max: usize,
}

impl Counter {
    fn new(max: usize) -> Counter {
        Counter { count: 0, max }
    }
}

impl Iterator for Counter {
    type Item = usize;
    
    fn next(&mut self) -> Option<Self::Item> {
        if self.count < self.max {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

fn main() {
    let counter = Counter::new(5);
    
    let sum: usize = counter.sum();
    println!("总和: {}", sum);  // 15
    
    let counter2 = Counter::new(5);
    let doubled: Vec<usize> = counter2.map(|x| x * 2).collect();
    println!("翻倍: {:?}", doubled);  // [2, 4, 6, 8, 10]
}

模块系统

模块基础

rust 复制代码
// src/lib.rs 或 src/main.rs

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {
            println!("添加到等待列表");
        }
        
        fn seat_at_table() {
            println!("安排就座");
        }
    }
    
    mod serving {
        fn take_order() {
            println!("点餐");
        }
        
        fn serve_order() {
            println!("上菜");
        }
    }
}

pub fn eat_at_restaurant() {
    // 绝对路径
    crate::front_of_house::hosting::add_to_waitlist();
    
    // 相对路径
    front_of_house::hosting::add_to_waitlist();
}

use 关键字

rust 复制代码
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

// 引入模块
use crate::front_of_house::hosting;

// 引入函数
use crate::front_of_house::hosting::add_to_waitlist;

// 引入多个项目
use std::collections::{HashMap, HashSet};

// 引入所有公共项目
use std::collections::*;

// 重命名
use std::fmt::Result as FmtResult;

fn main() {
    hosting::add_to_waitlist();
    add_to_waitlist();
}

文件分离

rust 复制代码
// src/lib.rs
mod front_of_house;  // 声明模块

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

// src/front_of_house.rs(或 src/front_of_house/mod.rs)
pub mod hosting;

// src/front_of_house/hosting.rs(或 src/front_of_house/hosting/mod.rs)
pub fn add_to_waitlist() {}

访问权限

rust 复制代码
mod outer {
    pub fn public_function() {
        println!("公开函数");
    }
    
    fn private_function() {
        println!("私有函数");
    }
    
    pub mod inner {
        pub fn inner_public() {
            println!("内部公开函数");
        }
        
        fn inner_private() {
            println!("内部私有函数");
        }
    }
}

fn main() {
    outer::public_function();
    // outer::private_function();  // 错误!私有
    outer::inner::inner_public();
    // outer::inner::inner_private();  // 错误!私有
}

常用集合

Vec 向量

rust 复制代码
fn main() {
    // 创建
    let mut v: Vec<i32> = Vec::new();
    let v2 = vec![1, 2, 3, 4, 5];
    
    // 添加元素
    v.push(1);
    v.push(2);
    v.push(3);
    
    // 读取元素
    let third = &v[2];  // 可能 panic
    let third = v.get(2);  // 返回 Option<&T>
    
    // 遍历
    for i in &v {
        println!("{}", i);
    }
    
    // 可变遍历
    for i in &mut v {
        *i += 50;
    }
    
    // 存储多种类型
    enum SpreadsheetCell {
        Int(i32),
        Float(f64),
        Text(String),
    }
    
    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Text(String::from("blue")),
        SpreadsheetCell::Float(10.12),
    ];
}

String 字符串

rust 复制代码
fn main() {
    // 创建
    let mut s = String::new();
    let s2 = "initial contents".to_string();
    let s3 = String::from("initial contents");
    
    // 追加
    s.push_str("hello");
    s.push('!');  // 单个字符
    
    // 拼接
    let s1 = String::from("Hello, ");
    let s2 = String::from("world!");
    let s3 = s1 + &s2;  // s1 被移动,s2 被借用
    
    // format! 宏
    let s1 = String::from("tic");
    let s2 = String::from("tac");
    let s3 = String::from("toe");
    let s = format!("{}-{}-{}", s1, s2, s3);
    
    // 索引(不支持!)
    // let c = s[0];  // 错误!
    
    // 切片(小心 UTF-8)
    let hello = "你好";
    // let s = &hello[0..1];  // 可能 panic!中文是多字节
    let s = &hello[0..3];  // 正确,一个中文字符占 3 字节
    
    // 遍历
    for c in "你好".chars() {
        println!("{}", c);  // 你,好
    }
    
    for b in "你好".bytes() {
        println!("{}", b);  // 每个字节
    }
}

HashMap<K, V> 哈希表

rust 复制代码
use std::collections::HashMap;

fn main() {
    // 创建
    let mut scores = HashMap::new();
    
    // 插入
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Red"), 50);
    
    // 访问
    let team_name = String::from("Blue");
    let score = scores.get(&team_name);  // Option<&V>
    
    // 遍历
    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }
    
    // 更新
    scores.insert(String::from("Blue"), 25);  // 覆盖
    
    // 只在键不存在时插入
    scores.entry(String::from("Yellow")).or_insert(50);
    scores.entry(String::from("Blue")).or_insert(50);  // 不会覆盖
    
    // 根据旧值更新
    let text = "hello world wonderful world";
    let mut map = HashMap::new();
    
    for word in text.split_whitespace() {
        let count = map.entry(word).or_insert(0);
        *count += 1;
    }
    
    println!("{:?}", map);
}

智能指针

Box 堆分配

rust 复制代码
fn main() {
    // 在堆上分配
    let b = Box::new(5);
    println!("b = {}", b);
    
    // 递归类型
    enum List {
        Cons(i32, Box<List>),
        Nil,
    }
    
    use List::{Cons, Nil};
    
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}

Rc 引用计数

rust 复制代码
use std::rc::Rc;

fn main() {
    // 多所有权
    let a = Rc::new(5);
    let b = Rc::clone(&a);  // 增加引用计数
    let c = Rc::clone(&a);  // 再增加
    
    println!("引用计数: {}", Rc::strong_count(&a));  // 3
    
    // 当所有引用离开作用域,数据才会被释放
}

RefCell 内部可变性

rust 复制代码
use std::cell::RefCell;

fn main() {
    // 在运行时检查借用规则,而不是编译时
    let x = RefCell::new(5);
    
    // 可变借用
    *x.borrow_mut() += 1;
    
    // 不可变借用
    println!("x = {}", *x.borrow());
    
    // 注意:运行时违反规则会 panic
    // let ref1 = x.borrow();
    // let ref2 = x.borrow_mut();  // panic!同时有可变和不可变借用
}

组合使用

rust 复制代码
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug)]
struct Node {
    value: i32,
    children: RefCell<Vec<Rc<Node>>>,
}

fn main() {
    let leaf = Rc::new(Node {
        value: 3,
        children: RefCell::new(vec![]),
    });
    
    let branch = Rc::new(Node {
        value: 5,
        children: RefCell::new(vec![Rc::clone(&leaf)]),
    });
    
    println!("branch: {:?}", branch);
}

并发编程

线程

rust 复制代码
use std::thread;
use std::time::Duration;

fn main() {
    // 创建线程
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("子线程: {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    
    for i in 1..5 {
        println!("主线程: {}", i);
        thread::sleep(Duration::from_millis(1));
    }
    
    // 等待线程结束
    handle.join().unwrap();
}

move 闭包

rust 复制代码
use std::thread;

fn main() {
    let v = vec![1, 2, 3];
    
    // move 把所有权转移到线程中
    let handle = thread::spawn(move || {
        println!("向量: {:?}", v);
    });
    
    // println!("{:?}", v);  // 错误!v 已经被移动
    
    handle.join().unwrap();
}

消息传递

rust 复制代码
use std::sync::mpsc;
use std::thread;

fn main() {
    // 创建通道
    let (tx, rx) = mpsc::channel();
    
    thread::spawn(move || {
        let val = String::from("你好");
        tx.send(val).unwrap();
        // println!("{}", val);  // 错误!val 已经被移动
    });
    
    // 接收消息(阻塞)
    let received = rx.recv().unwrap();
    println!("收到: {}", received);
    
    // 多个发送者
    let (tx, rx) = mpsc::channel();
    let tx1 = tx.clone();
    
    thread::spawn(move || {
        let vals = vec![
            String::from("你好"),
            String::from("来自"),
            String::from("线程"),
        ];
        
        for val in vals {
            tx1.send(val).unwrap();
        }
    });
    
    thread::spawn(move || {
        let vals = vec![
            String::from("更多"),
            String::from("消息"),
        ];
        
        for val in vals {
            tx.send(val).unwrap();
        }
    });
    
    // 迭代接收
    for received in rx {
        println!("收到: {}", received);
    }
}

共享状态

rust 复制代码
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    // Mutex:互斥锁
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    
    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("结果: {}", *counter.lock().unwrap());
}

常见陷阱与技巧

1. 所有权陷阱

rust 复制代码
fn main() {
    let v = vec![1, 2, 3];
    let v2 = v;  // 移动
    // println!("{:?}", v);  // 错误!v 已经无效
    
    // 解决方案 1:克隆
    let v3 = vec![1, 2, 3];
    let v4 = v3.clone();
    println!("{:?} {:?}", v3, v4);  // 都有效
    
    // 解决方案 2:引用
    let v5 = vec![1, 2, 3];
    let v6 = &v5;
    println!("{:?} {:?}", v5, v6);  // 都有效
}

2. 借用检查器陷阱

rust 复制代码
fn main() {
    let mut v = vec![1, 2, 3];
    
    // 错误示例
    // let first = &v[0];  // 不可变借用
    // v.push(4);          // 可变借用
    // println!("{}", first);  // 错误!同时有可变和不可变借用
    
    // 正确做法
    let first = v[0];
    v.push(4);
    println!("{}", first);
}

3. 生命周期陷阱

rust 复制代码
// 错误示例
// fn longest(x: &str, y: &str) -> &str {
//     let result = String::from("really long string");
//     result.as_str()  // 错误!result 在函数结束时被释放
// }

// 正确做法
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

4. 字符串陷阱

rust 复制代码
fn main() {
    let s = "你好世界";
    
    // 错误:中文字符占 3 字节
    // let c = &s[0..1];  // panic!
    
    // 正确做法
    let c = &s[0..3];  // "你"
    println!("{}", c);
    
    // 更好的做法:使用 chars()
    for c in s.chars() {
        println!("{}", c);
    }
}

5. 整数溢出

rust 复制代码
fn main() {
    // debug 模式会 panic,release 模式会回绕
    let mut x: u8 = 255;
    // x += 1;  // debug: panic, release: 0
    
    // 显式处理
    x = x.checked_add(1).unwrap_or(0);  // 溢出返回 None
    x = x.saturating_add(1);  // 溢出保持最大值
    x = x.wrapping_add(1);  // 溢出回绕
}

6. 使用 Option 和 Result

rust 复制代码
fn main() {
    // 不要用 unwrap(),用 ? 或 match
    // let x = some_option.unwrap();  // 可能 panic
    
    // 更好的做法
    let x = match some_option {
        Some(v) => v,
        None => return,
    };
    
    // 或者
    let x = some_option.ok_or("错误")?;
}

7. 避免克隆

rust 复制代码
fn main() {
    // 不要过度使用克隆
    let s1 = String::from("hello");
    let s2 = s1.clone();  // 有时是必要的,但考虑是否可以用引用
    
    // 使用引用
    fn print_string(s: &str) {
        println!("{}", s);
    }
    
    print_string(&s1);  // 不需要克隆
}

8. 使用迭代器代替循环

rust 复制代码
fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    
    // 不推荐
    let mut sum = 0;
    for n in &numbers {
        sum += n;
    }
    
    // 推荐
    let sum: i32 = numbers.iter().sum();
    
    // 链式操作
    let result: Vec<i32> = numbers
        .iter()
        .filter(|x| *x % 2 == 0)
        .map(|x| x * 2)
        .collect();
}

9. 使用 ? 传播错误

rust 复制代码
use std::fs::File;
use std::io::Read;

// 不推荐
fn read_file() -> Result<String, std::io::Error> {
    let f = File::open("data.txt");
    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };
    
    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

// 推荐
fn read_file_better() -> Result<String, std::io::Error> {
    let mut s = String::new();
    File::open("data.txt")?.read_to_string(&mut s)?;
    Ok(s)
}

// 最推荐
fn read_file_best() -> Result<String, std::io::Error> {
    std::fs::read_to_string("data.txt")
}

10. 使用 derive 减少样板代码

rust 复制代码
// 使用 derive 自动实现常用 trait
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Person {
    name: String,
    age: u32,
}

fn main() {
    let p1 = Person {
        name: String::from("张三"),
        age: 25,
    };
    
    let p2 = p1.clone();
    
    println!("{:?}", p1);  // Debug
    println!("{}", p1 == p2);  // PartialEq
}

总结

Rust 的核心概念:

  1. 所有权 - 每个值有且只有一个所有者,离开作用域自动释放
  2. 借用 - 通过引用临时访问值,不获取所有权
  3. 生命周期 - 确保引用始终有效,防止悬垂指针

学习路线建议

rust 复制代码
入门阶段(1-2周)
├── 基本语法(变量、函数、控制流)
├── 所有权系统(重点!)
├── 引用与借用
└── 结构体和枚举

进阶阶段(2-4周)
├── 模式匹配
├── 错误处理(Option、Result)
├── 泛型和 Trait
├── 生命周期标注
└── 闭包和迭代器

实战阶段(持续)
├── 模块系统
├── 常用集合(Vec、HashMap)
├── 智能指针
├── 并发编程
└── 异步编程(async/await)

推荐资源

  1. 官方书籍:《The Rust Programming Language》(Rust 程序设计语言)
  2. 练习网站 :Rustlings(github.com/rust-lang/r...
  3. 实战项目 :Rust by Example(doc.rust-lang.org/rust-by-exa...
  4. 社区:Rust 中文社区、RustCC

常见编译错误速查

错误信息 原因 解决方法
borrow of moved value 值被移动后使用 使用引用 &.clone()
cannot borrow as mutable 不可变变量借为可变 mut 或重新设计
cannot borrow as mutable more than once 同时有多个可变引用 缩小引用作用域
lifetime may not live long enough 生命周期不明确 添加生命周期标注
use of possibly uninitialized variable 变量可能未初始化 确保所有路径都初始化

最后的话:Rust 确实有学习曲线,但一旦你理解了所有权系统,你会发现它其实很符合直觉。编译器虽然严格,但它是在帮你避免运行时错误。坚持写下去,你会发现 Rust 的魅力!

祝你学习愉快!🦀

相关推荐
山佳的山2 小时前
KingbaseES 共享锁(SHARE)与排他锁(EXCLUSIVE)详解及测试复现
后端
程序员清风2 小时前
程序员兼职必看:靠谱软件外包平台挑选指南与避坑清单!
java·后端·面试
鱼人3 小时前
MySQL 实战入门:从“增删改查”到“高效查询”的核心指南
后端
大鹏19883 小时前
告别 Session:Spring Boot 实现 JWT 无状态登录认证全攻略
后端
Java编程爱好者3 小时前
从 AQS 到 ReentrantLock:搞懂同步队列与条件队列,这一篇就够了
后端
鱼人3 小时前
Nginx 全能指南:从反向代理到负载均衡,一篇打通任督二脉
后端
UIUV3 小时前
node:child_process spawn 模块学习笔记
javascript·后端·node.js
Java编程爱好者3 小时前
如果明天 Spring 框架突然从世界上消失,Java 会发生什么?
后端
神奇小汤圆4 小时前
Spring让Java慢了30倍,JIT、AOT等让Java比Python快13倍,比C慢17%
后端