Rust 是一门注重内存安全、性能和并发性的系统编程语言。以下是其核心语法的扩展介绍,涵盖更多细节和实用特性,适合初学者快速了解并深入掌握。

1. 变量与常量

  • 变量:使用 let 声明,默认不可变。使用 mut 允许修改。变量可以被遮蔽(重新声明)。

    let x = 5;
    let x = x + 1; // 遮蔽,创建新变量
    let mut y = 10;
    y = 15;

    常见场景与注意事项:

    • 配置常量与全局值:不可变配置信息用 conststatic,但如果需要延迟初始化或线程安全的可变全局,优先使用 once_cell::sync::Lazylazy_static + Mutex
    • 遮蔽(shadowing)用于按步骤转换值(例如 let s = s.trim();),可以减少可变性并限定作用域。
    • 谨慎使用 static mut —— 这是不安全的;如需全局可变状态请使用 Atomic* 类型或加锁封装。
    • 类型推断与转换:Rust 编译器能自动推断大多数类型,但在复杂情况下需要显式声明。
      let x = 5;          // 推断为 i32
      let y: f64 = 3.14;  // 显式声明类型
      let z = x as f64;   // 类型转换
  • 常量:使用 const 声明,需指定类型,全局作用域,值在编译时确定。

    const MAX_VALUE: u32 = 100_000; // 下划线提高数字可读性

    常见场景与注意事项:

    • const 适用于编译期已知、不依赖运行时环境的常量(如默认缓冲大小、协议常量)。
    • 如果需要运行时初始化的全局不可变值,使用 once_cell::sync::Lazy 初始化 static
  • 静态变量:使用 static 声明,生命周期贯穿程序运行。

    static GREETING: &str = "Hello, Rust!";

    常见场景与注意事项:

    • static 保存静态字符串、表驱动数据或与 FFI 交互时的固定地址数据。
    • 若需要可变静态数据,请使用 static 搭配线程安全原语(如 Atomic*Mutex)或 Lazy 延迟初始化。
    • 类型别名:使用 type 关键字创建类型别名,提高代码可读性。
      type Kilometers = i32;
      type Result<T> = std::result::Result<T, String>;
    • 字面量后缀:使用后缀明确指定数值类型。
      let x = 42u8;   // u8 类型
      let y = 3.14f32; // f32 类型
      let z = 100_000i64; // i64 类型,使用下划线提高可读性

2. 数据类型

  • 标量类型
    • 整数:有符号 (i8, i16, i32, i64, i128),无符号 (u8, u16, u32, u64, u128),以及 isize/usize(与系统架构相关)。
    • 浮点数:f32, f64
    • 布尔:bool (true, false)。
    • 字符:char (Unicode,单引号,如 '😊')。
  • 复合类型
    • 元组:固定长度,异构类型,可通过 .0, .1 访问或解构。
      let tup: (i32, f64, char) = (500, 6.4, 'z');
      let (x, y, z) = tup; // 解构
    • 数组:固定长度,同构类型,栈分配。
      let arr: [i32; 5] = [1, 2, 3, 4, 5];
      let first = arr[0];
    • 向量:动态数组,使用标准库 Vec<T>,堆分配。
      let mut v = vec![1, 2, 3];
      v.push(4);
    • 切片(Slice):对连续序列的"视图",不拥有数据,长度在运行时确定。常用于字符串与数组的局部访问,避免拷贝。
      let arr = [1, 2, 3, 4, 5];
      let slice: &[i32] = &arr[1..4];   // 包含 1..3,即 [2, 3, 4]
      println!("{:?}", slice);          // [2, 3, 4]
        
      let s = String::from("hello rust");
      let hello: &str = &s[0..5];       // 字符串切片
      println!("{}", hello);            // hello
      • 切片类型写作 &[T]&str,自带长度信息,可与泛型配合写出通用算法。
      • 空切片 &[] 合法,可用于表示“无数据”而无需 Option
      • 切片与借用规则一致:同一时刻只能有一个可变切片或多个不可变切片。

常见场景与注意事项:

  • 字符串边界:处理 UTF-8 字符串时不要盲目按字节切片(&s[0..n]),这可能截断多字节字符;使用 char 迭代或 s.get(..n) 做安全检查。
  • 数组 vs Vec:如果长度固定且小且无需堆分配,优先用数组;若涉及动态增长或可变长度,使用 Vec<T> 并考虑 with_capacity 以减少重分配。
  • API 设计:函数接受 &[T]&str 更通用,可以接收数组、切片和向量的引用,减少拷贝。
  • 字符串类型
    • String:可增长的 UTF-8 字符串,拥有数据。
    • &str:字符串切片,不拥有数据。
    let s1 = String::from("hello");
    let s2 = "world";           // &str 类型
    let s3 = s1 + " " + s2;     // String 拼接
    let len = s3.len();         // 获取字节长度(非字符数)
  • 类型转换
    let num_str = "42";
    let num: i32 = num_str.parse().unwrap();  // 字符串转数字
    let str_from_num = num.to_string();       // 数字转字符串

3. 函数

  • 函数使用 fn 定义,参数和返回值需显式指定类型。无返回值时省略 ->
    fn add(a: i32, b: i32) -> i32 {
        a + b // 表达式返回值,无需 return
    }
  • 发散函数:永不返回,使用 ! 作为返回类型(如 panic!)。
    fn diverge() -> ! {
        panic!("This function never returns!");
    }

常见场景与注意事项:

  • 小函数优先:把复杂逻辑拆成小函数,提高可测试性与可读性,便于单元测试。
  • 错误传播与返回类型:库函数应尽量返回 Result<T, E>,避免在库内部 panic!,将不可恢复错误留在应用层或测试。
  • 函数签名设计:如果函数需要灵活接受所有权或引用,提供多种签名(例如接收 &str 而非 String),或写成泛型以适配更多输入。
  • 函数指针:可以将函数作为参数传递或存储在变量中。
    fn add(a: i32, b: i32) -> i32 { a + b }
    let operation: fn(i32, i32) -> i32 = add;
    let result = operation(5, 3);
  • 高阶函数:接受函数作为参数或返回函数的函数。
    fn apply_twice<F>(f: F, x: i32) -> i32
    where F: Fn(i32) -> i32 {
        f(f(x))
    }
    let result = apply_twice(|x| x + 1, 5); // 7
  • 返回语句:虽然 Rust 是表达式语言,但也可以使用显式返回。
    fn early_return(x: i32) -> i32 {
        if x > 10 {
            return x * 2;  // 提前返回
        }
        x + 1  // 隐式返回
    }

4. 控制流

  • 条件语句
    • if 支持表达式风格返回值,无需三元运算符。
      let number = 7;
      let result = if number > 0 { "positive" } else { "non-positive" };
    • 条件必须是 bool 类型。
  • 循环
    • loop:无限循环,可用 break 返回值。
      let mut counter = 0;
      let result = loop {
          counter += 1;
          if counter == 10 {
              break counter * 2;
          }
      };
    • while:条件循环。
      while counter < 15 {
          counter += 1;
      }
    • for:常用于迭代器或范围。
      for i in 0..5 { // 包含 0,不包含 5
          println!("i: {}", i);
      }
    • Range(范围)语法:Rust 提供了多种范围表达式,用于创建序列和切片。
      • 半开区间 start..end:包含 start,不包含 end
        for i in 1..5 {  // 1, 2, 3, 4
            println!("{}", i);
        }
      • 闭区间 start..=end:包含 start 和 end
        for i in 1..=5 {  // 1, 2, 3, 4, 5
            println!("{}", i);
        }
      • 从起点开始 ..end:从默认起点到 end(不包含)
        let arr = [10, 20, 30, 40, 50];
        let slice = &arr[..3];  // [10, 20, 30]
      • 到终点结束 start..:从 start 到终点(包含)
        let arr = [10, 20, 30, 40, 50];
        let slice = &arr[2..];  // [30, 40, 50]
      • 全范围 ..:包含所有元素
        let arr = [10, 20, 30, 40, 50];
        let slice = &arr[..];  // [10, 20, 30, 40, 50]
      • Range 类型
        • Range<T>start..end
        • RangeInclusive<T>start..=end
        • RangeFrom<T>start..
        • RangeTo<T>..end
        • RangeToInclusive<T>..=end
        • RangeFull..
      use std::ops::{Range, RangeInclusive, RangeFrom, RangeTo, RangeToInclusive, RangeFull};
      
      let r1: Range<i32> = 1..5;
      let r2: RangeInclusive<i32> = 1..=5;
      let r3: RangeFrom<i32> = 3..;
      let r4: RangeTo<i32> = ..5;
      let r5: RangeToInclusive<i32> = ..=5;
      let r6: RangeFull = ..;
      • Range 迭代:Range 实现了 Iterator trait,可以直接用于循环
        // 倒序迭代
        for i in (1..=5).rev() {
            println!("{}", i);  // 5, 4, 3, 2, 1
        }
        
        // 步长迭代(使用 step_by)
        for i in (0..10).step_by(2) {
            println!("{}", i);  // 0, 2, 4, 6, 8
        }

常见场景与注意事项:

  • 使用 if 表达式构造值可以避免可变变量,比如 let status = if cond { a } else { b };
  • 选择合适的迭代方式:iter()(借用)、iter_mut()(可变借用)、into_iter()(消费并取得所有权),以避免所有权相关编译错误。
  • loop + break 值在需要复杂退出条件且最终返回计算结果时更清晰。
  • 模式匹配在控制流中的应用
    let pair = (0, -2);
    match pair {
        (0, y) => println!("x=0, y={}", y),
        (x, 0) => println!("x={}, y=0", x),
        _ => println!("其他情况"),
    }
  • 标签循环:可以给循环添加标签,用于明确指定 break/continue 的目标。
    'outer: for i in 0..3 {
        'inner: for j in 0..3 {
            if i == 1 && j == 1 {
                break 'outer;
            }
            println!("({}, {})", i, j);
        }
    }

5. 输入输出(I/O)

Rust 的标准库提供了丰富的输入输出功能,涵盖控制台、文件、网络等。I/O 操作通常返回 Result 类型,需要正确处理可能的错误。

5.1 标准输入输出

  • 标准输出:使用 println!print! 宏进行格式化输出。
    println!("Hello, {}!", "Rust");  // 自动换行
    print!("Enter your name: ");     // 不换行
  • 标准输入:使用 std::io 模块读取用户输入。
    use std::io;
    
    let mut input = String::new();
    io::stdin()
        .read_line(&mut input)
        .expect("Failed to read line");
    println!("You entered: {}", input.trim());
  • 格式化输出:支持多种格式化选项(已在第15章常用宏中详细介绍)。
    let x = 42;
    let y = 3.14159;
    println!("x = {:04}, y = {:.2}", x, y);  // x = 0042, y = 3.14

5.2 文件 I/O

  • 读取文件:使用 std::fs::read_to_string 读取整个文件为字符串。
    use std::fs;
    
    let content = fs::read_to_string("hello.txt")
        .expect("Failed to read file");
    println!("File content: {}", content);
  • 写入文件:使用 std::fs::write 写入字符串或字节。
    fs::write("output.txt", "Hello, file!")
        .expect("Failed to write file");
  • 逐行读取:使用 BufReader 高效读取大文件。
    use std::fs::File;
    use std::io::{BufRead, BufReader};
    
    let file = File::open("data.txt").expect("Failed to open file");
    let reader = BufReader::new(file);
    for line in reader.lines() {
        let line = line.expect("Failed to read line");
        println!("{}", line);
    }

5.3 缓冲读写

  • BufReader:减少系统调用,提高读取性能。
    use std::io::BufReader;
    use std::fs::File;
    
    let file = File::open("large.txt")?;
    let mut reader = BufReader::new(file);
    let mut buffer = String::new();
    reader.read_to_string(&mut buffer)?;
  • BufWriter:缓冲写入,减少写操作次数。
    use std::io::{BufWriter, Write};
    use std::fs::File;
    
    let file = File::create("output.txt")?;
    let mut writer = BufWriter::new(file);
    writer.write_all(b"Hello, buffered world!")?;
    writer.flush()?;  // 确保所有数据写入磁盘

5.4 错误处理

  • I/O 操作返回 Result<T, std::io::Error>,必须处理可能的错误。
  • 使用 ? 运算符传播错误,或使用 expect/unwrap 在简单程序中快速失败。
    use std::fs::File;
    use std::io::Read;
    
    fn read_file(path: &str) -> Result<String, std::io::Error> {
        let mut file = File::open(path)?;
        let mut content = String::new();
        file.read_to_string(&mut content)?;
        Ok(content)
    }

5.5 路径处理

  • 使用 std::path::PathPathBuf 进行跨平台路径操作。
    use std::path::Path;
    
    let path = Path::new("/home/user/file.txt");
    if path.exists() {
        println!("File exists");
    }

常见场景与注意事项:

  • 生产代码中避免使用 expect/unwrap,应妥善处理所有可能的错误。
  • 大文件使用流式读取(BufReader + 逐行/逐块)避免内存溢出。
  • 跨平台路径使用 Path/PathBuf 而非字符串拼接。
  • 网络 I/O 通常使用异步编程(见第19章异步编程)。

6. 所有权与借用

  • 所有权:每个值有唯一所有者,离开作用域时释放。移动后原变量失效。
    let s1 = String::from("hello");
    let s2 = s1; // s1 失效
    // println!("{}", s1); // 错误:s1 已移动
  • 借用:使用 & 创建不可变引用,&mut 创建可变引用。同一时间只能有一个可变引用或多个不可变引用。
    let mut s = String::from("hello");
    let r1 = &s; // 不可变借用
    // let r2 = &mut s; // 错误:不可同时有可变和不可变借用
  • 复制:实现了 Copy trait 的类型(如基本类型)不会移动。
    let x = 5;
    let y = x; // 复制,x 仍有效

常见场景与注意事项:

  • 函数参数设计:若函数不需要修改数据,使用 &T;若需要修改则 &mut T;若函数需要获得所有权(例如构建返回值),传 T
  • Clone 的成本:对大型结构频繁 clone() 会有性能开销,优先考虑借用或使用共享引用(Rc/Arc)。
  • 临时值借用:不要返回对局部 String&str —— 这会导致借用指向超出作用域的值(编译器错误)。
  • 常见陷阱:在同一作用域内同时持有不可变借用与可变借用(例如在遍历同时修改容器)会被编译器拒绝。
  • 借用检查器规则总结
    • 规则1:任何借用必须在其引用的值的生命周期内有效。
    • 规则2:可以同时存在多个不可变借用 (&T)。
    • 规则3:不能同时存在可变借用 (&mut T) 和任何其他借用。
    let mut s = String::from("hello");
    let r1 = &s;        // 不可变借用
    let r2 = &s;        // 另一个不可变借用 - 允许
    // let r3 = &mut s; // 错误:不能同时有可变借用
    println!("{} {}", r1, r2);
    // r1 和 r2 在这里不再使用
    let r3 = &mut s;    // 现在允许 - 之前的借用已结束

7. 结构体与枚举

  • 结构体
    • 命名结构体:
      struct Rectangle {
          width: u32,
          height: u32,
      }
      let rect = Rectangle { width: 30, height: 50 };
    • 元组结构体:
      struct Color(u8, u8, u8);
      let black = Color(0, 0, 0);
    • 方法:使用 impl 定义。
      impl Rectangle {
          fn area(&self) -> u32 {
              self.width * self.height
          }
      }
  • 枚举:支持复杂数据结构,常与 match 结合。
    enum Message {
        Quit,
        Move { x: i32, y: i32 },
        Write(String),
    }

常见场景与注意事项:

  • 枚举用于状态机建模(例如连接状态、任务状态),结合 match 可保证覆盖所有分支。
  • DTO 与业务实体:接口层返回轻量 DTO,避免将内部可变状态或锁直接暴露给调用者。
  • impl 方法签名:不可变操作使用 &self,需要修改使用 &mut self,并根据可能的错误返回 Result
  • 关联函数(不取 self 参数):
    impl Rectangle {
        fn square(size: u32) -> Self {
            Self { width: size, height: size }
        }
    }
    let sq = Rectangle::square(10);
  • 枚举的常见模式
    enum IpAddr {
        V4(u8, u8, u8, u8),
        V6(String),
    }
    let home = IpAddr::V4(127, 0, 0, 1);
    let loopback = IpAddr::V6(String::from("::1"));

8. 模式匹配

  • match:穷尽匹配,必须覆盖所有情况。
    let num = Some(4);
    match num {
        Some(x) if x % 2 == 0 => println!("Even: {}", x),
        Some(x) => println!("Odd: {}", x),
        None => println!("None"),
    }
  • if let:简化单一模式匹配。
    if let Some(x) = num {
        println!("Value: {}", x);
    }

常见场景与注意事项:

  • 使用 if letwhile let 处理单一分支或逐项解析,代码更简洁。
  • 在匹配复杂结构时使用解构({ x, y, .. }(_, _, tail)),并用 _/.. 忽略不必要字段。
  • 当有默认分支时尽量显式写出 _ => {},以便未来扩展时不遗漏处理。
  • 守卫条件(match guards):
    let num = Some(4);
    match num {
        Some(x) if x < 5 => println!("小于5: {}", x),
        Some(x) => println!("大于等于5: {}", x),
        None => println!("没有值"),
    }
  • 绑定模式
    let value = Some(10);
    if let Some(x) = value {
        println!("值是: {}", x);
    } else {
        println!("没有值");
    }
  • while let 循环
    let mut stack = vec![1, 2, 3];
    while let Some(top) = stack.pop() {
        println!("弹出: {}", top);
    }

9. 错误处理

  • Option:处理可能为空的值 (Some(T)None)。
    let opt: Option<i32> = Some(5);
    let value = opt.unwrap_or(0); // 解包或提供默认值
  • Result:处理可能出错的操作 (Ok(T)Err(E))。
    fn divide(a: i32, b: i32) -> Result<i32, String> {
        if b == 0 {
            Err(String::from("Divide by zero"))
        } else {
            Ok(a / b)
        }
    }
    let result = divide(10, 2).expect("Division failed"); // 成功返回 5
  • ? 运算符:简化错误传播。
    fn try_divide() -> Result<i32, String> {
        let result = divide(10, 0)?;
        Ok(result)
    }

常见场景与注意事项:

  • 传播错误:使用 ? 链式传播并在上层提供上下文(结合 thiserror / anyhow / eyre 等库)。
  • 错误类型设计:库内使用精确错误类型(enum Error),应用层可以用 anyhow::Error 简化处理。
  • Option vs Result:仅"可能不存在"用 Option,若需错误信息使用 Result
  • 使用 map_err, with_context 来附带额外上下文,便于排查问题。
  • 组合子方法
    let opt1: Option<i32> = Some(5);
    let opt2: Option<i32> = Some(10);
    let sum = opt1.and_then(|x| opt2.map(|y| x + y)); // Some(15)
    
    let result: Result<i32, &str> = Ok(5);
    let doubled = result.map(|x| x * 2); // Ok(10)
  • 错误类型转换
    fn parse_number(s: &str) -> Result<i32, std::num::ParseIntError> {
        s.parse::<i32>()
    }
    
    fn double_number(s: &str) -> Result<i32, String> {
        parse_number(s)
            .map(|n| n * 2)
            .map_err(|e| format!("解析失败: {}", e))
    }

10. 模块系统

  • 使用 mod 定义模块,pub 控制可见性,use 引入路径。
    mod front_of_house {
        pub mod hosting {
            pub fn add_to_waitlist() {}
        }
    }
    use crate::front_of_house::hosting;
    hosting::add_to_waitlist();
  • 文件系统模块:mod 声明会加载同名文件或目录中的代码。

常见场景与注意事项:

  • 目录布局:在库 crate 中把公共 API 暴露在 lib.rs 顶层,内部实现放子模块并只 pub 出必要接口。
  • re-export:使用 pub use 聚合导出,简化外部调用路径(例如 pub use crate::module::Type)。
  • 避免循环依赖:通过拆分模块、提取 trait 或把共享接口移动到顶层模块来解决。
  • 模块路径系统
    // 绝对路径
    crate::front_of_house::hosting::add_to_waitlist();
    // 相对路径
    front_of_house::hosting::add_to_waitlist();
    // 使用 super 访问父模块
    super::some_function();
  • 使用 as 关键字重命名
    use std::collections::HashMap as Map;
    let mut map = Map::new();
  • 通配符导入(谨慎使用):
    use std::collections::*;

11. 集合类型

  • Vec:动态数组。
    let mut v = Vec::new();
    v.push(1);
  • HashMap:键值对集合。
    use std::collections::HashMap;
    let mut scores = HashMap::new();
    scores.insert(String::from("Blue"), 10);

常见场景与注意事项:

  • Vec 性能:当已知大小时使用 Vec::with_capacity(n) 预分配以避免重复分配。
  • HashMap 常用技巧:使用 entry API(or_insert, or_insert_with)减少查找与初始化重复。
  • 并发集合:在多线程共享时可用 Arc<Mutex<HashMap<...>>> 或使用 dashmap 等并发数据结构减少锁竞争。
  • 其他常用集合
    use std::collections::{HashSet, BTreeMap, BTreeSet, VecDeque, BinaryHeap};
    
    // HashSet - 无序集合
    let mut set = HashSet::new();
    set.insert(1);
    set.insert(2);
    
    // VecDeque - 双端队列
    let mut deque = VecDeque::new();
    deque.push_front(1);
    deque.push_back(2);
    
    // BinaryHeap - 二叉堆(优先队列)
    let mut heap = BinaryHeap::new();
    heap.push(3);
    heap.push(1);
    heap.push(2);
    println!("{}", heap.peek().unwrap()); // 3
  • 迭代器适配器
    let v = vec![1, 2, 3, 4, 5];
    let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();
    let evens: Vec<&i32> = v.iter().filter(|x| *x % 2 == 0).collect();

12. 泛型与 trait

  • 泛型:提高代码复用性。
    fn largest<T: PartialOrd>(list: &[T]) -> &T {
        &list[0]
    }
  • trait:定义共享行为,类似接口。
    trait Summary {
        fn summarize(&self) -> String;
    }
    struct Article {
        content: String,
    }
    impl Summary for Article {
        fn summarize(&self) -> String {
            format!("Article: {}", self.content)
        }
    }

常见场景与注意事项:

  • 泛型约束:当约束复杂时使用 where 语句提高可读性。
  • Trait 对象:需要在运行时动态分派时使用 Box<dyn Trait>,注意 object-safe 规则(某些 trait 无法作为 trait 对象)。
  • 为通用库设计泛型接口时小心二次编译膨胀(code bloat),并考虑为常见类型提供专门实现。
  • 关联类型
    trait Container {
        type Item;
        fn get(&self, index: usize) -> Self::Item;
    }
    
    struct VecContainer<T> {
        items: Vec<T>,
    }
    
    impl<T: Clone> Container for VecContainer<T> {
        type Item = T;
        fn get(&self, index: usize) -> Self::Item {
            self.items[index].clone()
        }
    }
  • 默认实现
    trait Summary {
        fn summarize(&self) -> String {
            String::from("(Read more...)")
        }
    }

13. 生命周期

  • 确保引用有效,生命周期注解用 'a 表示。
    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
        if x.len() > y.len() { x } else { y }
    }

常见场景与注意事项:

  • 不要返回对局部变量的引用(会导致编译错误)。
  • 当生命周期签名复杂时,考虑返回拥有所有权的类型(如 String)或使用 Arc/Cow 来简化 API。
  • 在接口设计中尽量让调用方持有所有权或明确借用关系,减少不必要的生命周期注解暴露。
  • 生命周期省略规则(Lifetime Elision):
    Rust 编译器会自动应用三条规则来省略生命周期注解:
    1. 每个引用参数都有自己的生命周期参数
    2. 如果只有一个输入生命周期参数,它被赋予所有输出生命周期参数
    3. 如果有多个输入生命周期参数,但其中一个时 &self&mut self,那么 self 的生命周期被赋予所有输出生命周期参数
    // 编译器可以自动推断,无需显式生命周期注解
    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[..]
    }
  • 静态生命周期
    let s: &'static str = "I have a static lifetime.";

14. 并发

  • 线程:使用 std::thread
    use std::thread;
    let handle = thread::spawn(|| {
        println!("Running in a thread!");
    });
    handle.join().unwrap();
  • 消息传递:通过通道 (mpsc)。
    use std::sync::mpsc;
    let (tx, rx) = mpsc::channel();
    tx.send(42).unwrap();
    println!("Received: {}", rx.recv().unwrap());
  • 共享状态:使用 MutexArc.
    use std::sync::{Arc, Mutex};
    let counter = Arc::new(Mutex::new(0));

常见场景与注意事项:

  • 任务划分:IO 密集型使用异步模型(tokio),CPU 密集型使用线程池或 spawn_blocking
  • 共享计数器:若只是计数,优先使用原子类型(AtomicUsize)以减少锁开销。
  • 通道模式:mpsc 适合生产者-消费者;对性能敏感场景可用 crossbeam 提供更高性能的通道。
  • 避免死锁:尽量缩小锁的持有范围,并避免在持锁期间调用外部代码或再加锁。
  • 读写锁(RwLock):
    use std::sync::RwLock;
    
    let lock = RwLock::new(5);
    
    // 多个读者
    {
        let r1 = lock.read().unwrap();
        let r2 = lock.read().unwrap();
        println!("{}", *r1);
    } // 读锁在这里释放
    
    // 单个写者
    {
        let mut w = lock.write().unwrap();
        *w += 1;
    }
  • 条件变量(Condvar):
    use std::sync::{Arc, Mutex, Condvar};
    
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair2 = Arc::clone(&pair);
    
    std::thread::spawn(move || {
        let (lock, cvar) = &*pair2;
        let mut started = lock.lock().unwrap();
        *started = true;
        cvar.notify_one();
    });
    
    let (lock, cvar) = &*pair;
    let mut started = lock.lock().unwrap();
    while !*started {
        started = cvar.wait(started).unwrap();
    }

15. 常用宏

  • println!, format!:格式化输出。
  • vec!:快速创建向量。
  • panic!:触发程序崩溃。
  • todo!:标记未实现代码。
    let v = vec![1, 2, 3];
    println!("{:?}", v);

常见场景与注意事项:

  • 调试:使用 dbg!(expr) 快速打印表达式及其位置,便于排查。
  • 宏滥用:宏能生成重复代码,但会增加调试难度;如果函数或 trait 已能满足需求,优先使用函数/trait。
  • 自定义宏:在需要元编程或减少重复模板化代码(如注册、绑定)时使用,但注意错误消息友好性。
  • 格式化宏详解
    let name = "Rust";
    let version = 1.70;
    
    println!("语言: {}, 版本: {}", name, version);  // 基本格式化
    println!("二进制: {:b}", 10);                  // 1010
    println!("十六进制: {:x}", 255);               // ff
    println!("指针: {:p}", &name);                 // 内存地址
    println!("调试: {:?}", (1, 2, 3));             // 调试格式
  • 断言宏
    assert!(true);
    assert_eq!(2 + 2, 4);
    assert_ne!(1, 2);
    debug_assert!(some_condition);  // 仅在 debug 模式下检查

16. 智能指针

Rust 中的“智能指针”不仅仅是指针,它们还可以提供额外的所有权语义与运行时行为。常见的智能指针包括 Box<T>Rc<T>Arc<T>Cell<T>RefCell<T>。下面分别介绍它们的用途、约束与示例。

  • Box:将值放到堆上,最简单的智能指针。常用于:

    • 在编译时大小不确定但需要在堆上存储(如递归数据结构)。
    • 明确将大对象移到堆以减小栈开销。
    let b = Box::new(5);
    let boxed_array: Box<[i32]> = Box::new([1, 2, 3]);
  • 常见场景与注意事项:

    • 用于实现递归数据结构(例如自定义链表和树),把子节点放在 Box 中以满足大小已知要求。
    • 在 API 需要明确传递堆上数据的所有权时使用 Box<T>
  • Rc(Reference Counted):引用计数智能指针,适用于单线程场景,允许多个所有者共享同一块数据。Rc<T> 在克隆时会增加引用计数,直到所有者都被丢弃,数据才会被释放。注意:Rc<T> 不能用于跨线程共享(不是 Send/Sync)。

    • 使用场景:树结构、图结构或需要多个所有者但只在单线程中的共享数据。
    use std::rc::Rc;
    
    let a = Rc::new(String::from("hello"));
    let b = Rc::clone(&a); // 增加引用计数
    println!("count = {}", Rc::strong_count(&a)); // 2
  • 常见场景与注意事项:

    • RefCell<T> 搭配使用:Rc<RefCell<T>> 常用于单线程内既需多所有者又需内部可变性的结构(如可变树)。
    • 注意循环引用会导致内存泄漏;若需要父指针请使用 Weak 断开循环。
  • Arc(Atomic Reference Counted):与 Rc<T> 类似,但使用原子操作维护引用计数,因此可以安全地在多线程间共享(实现了 SendSync,前提是 T 本身满足相应约束)。相比 RcArc 的计数开销更高,但可用于并发场景。

    use std::sync::Arc;
    use std::thread;
    
    let data = Arc::new(vec![1, 2, 3]);
    let mut handles = Vec::new();
    for _ in 0..3 {
        let d = Arc::clone(&data);
        handles.push(thread::spawn(move || {
            println!("{:?}", d);
        }));
    }
    for h in handles {
        h.join().unwrap();
    }
  • 常见场景与注意事项:

    • 在多线程共享读/写场景中通常与 MutexRwLock 结合(Arc<Mutex<T>>Arc<RwLock<T>>)。
    • 当仅需要只读共享时,优先使用 Arc<T>(且 T 为不可变或只读)以减少锁开销。
  • Cell与 RefCell(内部可变性 / Interior Mutability):它们允许在外部不可变绑定下对内部数据进行修改(绕开编译时的借用规则,但在运行时仍保证安全性)。

    • Cell<T>:提供按值存取(Copy 或可移动类型),常用方法有 get / set / replace。不提供引用借用;适合小型标量或实现缓存的场景。
      use std::cell::Cell;
      
      let c = Cell::new(5);
      c.set(10);
      let v = c.get();
    • RefCell<T>:在单线程中用于在运行时进行借用检查。通过 borrow()(不可变借用,返回 Ref<T>)和 borrow_mut()(可变借用,返回 RefMut<T>)实现。当出现违反借用规则的情况(例如同时存在一个 borrow() 和一个 borrow_mut()),会在运行时 panic。
      • 常见搭配:Rc<RefCell<T>>,用于在单线程中既需要多个所有者(Rc)又需要在任意时间点可变访问(RefCell)的场景,例如构建可变的树/图。
      use std::rc::Rc;
      use std::cell::RefCell;
      
      #[derive(Debug)]
      struct Node {
          value: i32,
          children: Vec<Rc<RefCell<Node>>>,
      }
      
      let leaf = Rc::new(RefCell::new(Node { value: 3, children: vec![] }));
      let branch = Rc::new(RefCell::new(Node { value: 5, children: vec![Rc::clone(&leaf)] }));
      // 修改 leaf 的值,即使它被多个 Rc 所拥有
      leaf.borrow_mut().value = 10;
      println!("{:?}", branch);
  • 设计和使用要点(实践建议):

    • 优先使用编译时借用规则(& / &mut)。只有在确实需要多个所有者或内部可变性时,才使用智能指针。
    • 使用 Rc<T> 时注意循环引用(reference cycle)会导致内存泄漏,因为引用计数永远无法降为 0。若确实需要父子双向引用,可用 Weak<T>(弱引用)来打破循环:
      use std::rc::{Rc, Weak};
      use std::cell::RefCell;
      
      struct Node { parent: RefCell<Weak<Node>>, children: RefCell<Vec<Rc<Node>>> }
    • RefCell<T> 在运行时检查借用,错误会导致 panic。若需要在运行时避免 panic,应在逻辑上保证借用规则或使用更合适的并发原语(如 Mutex)。
    • 在多线程场景下,使用 Arc<T> + Mutex<T>Arc<RwLock<T>> 来实现线程安全的可变共享数据,而不是 Rc / RefCell
    • Cow(写时克隆)
      use std::borrow::Cow;
      
      fn remove_spaces<'a>(input: &'a str) -> Cow<'a, str> {
          if input.contains(' ') {
              Cow::Owned(input.replace(' ', ""))
          } else {
              Cow::Borrowed(input)
          }
      }
    • Pin 和 Unpin(高级特性):
      用于确保某些类型在内存中的位置不会被移动,主要用于异步编程。
      use std::pin::Pin;
      
      let mut val = 5;
      let pinned = Pin::new(&mut val);

小结:

  • Box:将值放到堆上(所有权明确)。
  • Rc:单线程引用计数,多所有者(只读或配合内部可变性)。
  • Arc:跨线程的原子引用计数。
  • Cell / RefCell:在单线程下提供“内部可变性”,在运行时检查借用规则(RefCell 会 panic 当规则被违反)。
  • Weak:与 Rc/Arc 配合使用,避免循环引用,不能增加引用计数但可以升级为强引用(若仍存活)。

17. 迭代器与闭包

  • 迭代器(Iterator):Rust 的 for 循环本质就是迭代器语法糖。任何实现 Iterator 的类型都可以用 for 遍历。
    let v = vec![1, 2, 3];
    let mut iter = v.iter();   // 产生 &i32
    assert_eq!(iter.next(), Some(&1));
    • 常用适配器:mapfiltercollectfoldenumeratezip 等,均返回新迭代器,实现“零成本抽象”。
    let sum: i32 = (1..=10).filter(|&x| x % 2 == 0).sum();
    • 自定义迭代器:实现 Iterator 关联类型 Itemnext 方法即可。
  • 闭包(Closure):匿名函数,可捕获环境变量,语法 |param| body
    let factor = 2;
    let multiply = |x| x * factor;
    assert_eq!(multiply(5), 10);
    • 闭包会根据捕获方式自动实现 FnFnMutFnOnce 三种 trait,决定可被调用多次/可变/一次性。
    • 常与迭代器搭配,实现链式数据处理:
    let v: Vec<i32> = vec![1, 2, 3]
        .into_iter()
        .map(|x| x * x)
        .collect();

常见场景与注意事项:

  • 流式处理:用迭代器链实现数据转换流水线,既简洁又高效(惰性求值)。
  • 惰性计算:迭代器直到被消费(如 collectsumfor)才执行,适合处理大数据/流式数据。
  • 捕获语义:闭包默认按需借用或移动捕获变量;遇到所有权冲突时可使用 move 强制移动捕获。
  • 迭代器消费者
    let v = vec![1, 2, 3, 4, 5];
    
    // 消费迭代器并产生结果
    let sum: i32 = v.iter().sum();
    let product: i32 = v.iter().product();
    let max: Option<&i32> = v.iter().max();
    let min: Option<&i32> = v.iter().min();
    
    // 检查条件
    let all_positive = v.iter().all(|&x| x > 0);
    let any_large = v.iter().any(|&x| x > 3);
  • 闭包 trait 详解
    • Fn:可以多次调用,不获取所有权
    • FnMut:可以多次调用,可能修改环境
    • FnOnce:只能调用一次,获取所有权
    fn apply<F>(f: F) -> i32
    where F: Fn(i32) -> i32 {
        f(5)
    }
    
    let x = 10;
    let add_x = |y| y + x;  // 实现 Fn
    println!("{}", apply(add_x)); // 15

18. 属性与条件编译

  • 属性(Attribute):以 #[...] 形式给编译器提供元数据。
    #[derive(Debug, Clone)]
    struct Point { x: f64, y: f64 }
    
    #[inline(always)]
    fn fast_add(a: i32, b: i32) -> i32 { a + b }
  • 条件编译:使用 #[cfg(...)] 按平台/特性包含或排除代码。
    #[cfg(target_os = "linux")]
    fn on_linux() { println!("Running on Linux"); }
    
    #[cfg(feature = "serde")]
    impl serde::Serialize for Config { /* ... */ }
    • Cargo.toml 中通过 [features] 定义可选特性,实现“按需编译”。

常见场景与注意事项:

  • 平台相关分支:用 #[cfg(...)] 为不同平台实现差异化逻辑,保持同一 API。
  • 可选依赖:在 Cargo.toml 使用 feature 将可选功能隔离,减少默认依赖体积(例如 serde)。
  • 测试特定代码:用 #[cfg(test)] 隔离测试辅助代码,避免污染生产可执行文件。
  • 自定义属性
    // 自定义条件编译属性
    #[cfg_attr(feature = "nightly", feature(specialization))]
    fn specialized_function() {}
    
    // 标记废弃
    #[deprecated(since = "1.0.0", note = "请使用 new_function 代替")]
    fn old_function() {}
    
    // 允许特定警告
    #[allow(dead_code)]
    fn unused_function() {}
  • 内联属性详解
    #[inline]          // 建议内联
    #[inline(always)]  // 总是内联
    #[inline(never)]   // 从不内联
    fn example() {}

19. 异步编程

  • async/.await:Rust 使用零成本抽象的异步模型,基于 Future trait。
    use tokio; // 常用运行时
    
    #[tokio::main]
    async fn main() {
        let task = async { 42 };
        let val = task.await;
        println!("{}", val);
    }
    • async fn 返回实现 Future 的匿名类型;.await 挂起当前任务,让出线程。
    • 并发原语:join!(并发等待多个 Future)、select!(多路复用)、spawn(任务调度)。
    • 注意:异步块默认单线程调度,CPU 密集任务应使用 spawn_blocking 或专用线程池。

常见场景与注意事项:

  • 运行时选择:在应用层使用 tokio 或 async-std;库层尽量不直接依赖特定运行时以提高可组合性(使用 futures 兼容 trait)。
  • IO 密集型服务:HTTP、数据库访问、网络爬虫等使用 async 可以获得高并发吞吐。
  • 阻塞操作:避免在 async 任务中直接执行阻塞 IO 或 CPU 密集任务,使用 spawn_blocking 或单独线程池。
  • Future trait 基础
    use std::future::Future;
    use std::pin::Pin;
    use std::task::{Context, Poll};
    
    struct MyFuture {
        count: u32,
    }
    
    impl Future for MyFuture {
        type Output = u32;
        
        fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
            if self.count > 0 {
                self.count -= 1;
                Poll::Pending
            } else {
                Poll::Ready(42)
            }
        }
    }
  • 异步 trait(实验性):
    #[async_trait::async_trait]  // 需要 async-trait crate
    trait AsyncService {
        async fn process(&self, data: &str) -> String;
    }

20. 宏系统

  • 声明宏(macro_rules!):基于模式匹配生成代码。
    macro_rules! say_hello {
        () => { println!("Hello!"); };
        ($name:expr) => { println!("Hello, {}!", $name); };
    }
    say_hello!();
    say_hello!("Rust");
  • 过程宏(Procedural Macro):分为 #[derive(...)]#[attribute]#[function_like] 三种,通过 proc_macro crate 实现,用于代码生成、DSL 等高级场景。
    • 示例:自定义 #[derive(MyTrait)] 为结构体自动生成实现。

常见场景与注意事项:

  • 使用 derive 为常见 trait(Debug, Clone, Serialize, Deserialize)自动生成实现,减少模板代码。
  • 过程宏适合生成重复模板代码或实现 DSL,但编写与调试更复杂,务必提供清晰的错误信息与文档。
  • 宏会使错误信息与展开代码更难调试,原则上优先用函数、trait、泛型实现可行的逻辑。
  • 过程宏示例
    // derive 宏
    #[derive(Debug, Clone, PartialEq)]
    struct Point { x: i32, y: i32 }
    
    // attribute 宏
    #[tokio::main]
    async fn main() {}
    
    // function-like 宏
    let v = vec![1, 2, 3];  // vec! 是声明宏
  • 宏的卫生性(Hygiene):
    Rust 宏是卫生的,意味着它们不会意外捕获外部作用域的标识符。
    macro_rules! make_function {
        () => {
            fn helper() -> i32 { 42 }
        };
    }
    
    make_function!();
    // helper(); // 错误:helper 不在作用域中

21. 工具链与生态

  • Cargo:Rust 的构建系统与包管理器。
    • cargo newbuildruntestcheckclippyfmt 等常用命令。
    • Cargo.toml 声明依赖、特性、工作区;Cargo.lock 锁定版本,确保可重复构建。
  • crates.io:官方包仓库,发布与获取开源库。
  • rustup:工具链管理器,支持安装/切换 stable/beta/nightly 版本,以及交叉编译目标。
  • 常用开发工具
    • rustfmt:自动格式化代码。
    • clippy:Lint 工具,提供 500+ 规则检查。
    • rust-analyzer:IDE 语言服务器,支持 VS Code、Vim、Emacs 等。
  • 测试体系
    • 单元测试:与源码同文件,置于 #[cfg(test)] mod tests
    • 集成测试:放在 tests/ 目录,独立可执行。
    • 文档测试:在 /// 注释中写 ``` 代码块,cargo test 自动运行。
  • 文档生成cargo doc --open 生成并打开 HTML 文档,支持 Markdown 与交叉链接。
  • 性能调优
    • cargo bench:使用 #[bench] 进行基准测试(需 nightly)。
    • perf/flamegraph:采样分析;cargo profiler 集成火焰图。
    • #[inline]#[cold]std::hint::unreachable_unchecked() 等微优化提示。
  • 安全与审计
    • cargo audit:检查依赖已知漏洞。
    • cargo geiger:统计 unsafe 代码占比。
    • cargo deny:强制许可证/漏洞/重复依赖策略。

常见场景与注意事项:

  • CI 集成:在 CI 中强制运行 cargo fmt -- --checkcargo clippy -- -D warningscargo test 保持代码质量。
  • 依赖审计:使用 cargo audit 定期扫描依赖漏洞,尤其在生产部署前。
  • 本地开发:使用 rustup 管理工具链并在项目中记录所需的 toolchain(rust-toolchain.toml),保证团队一致性。
  • 性能剖析:在性能热点使用采样工具(perf + flamegraph)而非盲目微优化;仅在确认热点后才用 #[inline] 等属性。
  • 工作空间(Workspace)
    # Cargo.toml
    [workspace]
    members = [
        "crate-a",
        "crate-b",
    ]
  • 发布配置优化
    # Cargo.toml
    [profile.release]
    lto = true          # 启用链接时优化
    codegen-units = 1   # 单代码生成单元
    panic = "abort"     # 移除 panic 处理代码
  • 常用开发命令
    cargo tree          # 查看依赖树
    cargo expand        # 查看宏展开结果
    cargo metadata      # 获取项目元数据
    cargo fix           # 自动修复编译警告
    cargo miri          # 运行 Miri 检测未定义行为
  • 交叉编译
    # 安装目标平台
    rustup target add x86_64-unknown-linux-musl
    
    # 为特定目标构建
    cargo build --target x86_64-unknown-linux-musl
  • 自定义构建脚本
    // build.rs
    fn main() {
        // 告诉 Cargo 在重新运行构建脚本时,如果 src/hello.c 改变则重新运行
        println!("cargo:rerun-if-changed=src/hello.c");
        
        // 使用 cc crate 编译 C 代码
        cc::Build::new()
            .file("src/hello.c")
            .compile("hello");
    }
  • 环境变量
    // 在编译时获取环境变量
    const VERSION: &str = env!("CARGO_PKG_VERSION");
    const NAME: &str = env!("CARGO_PKG_NAME");
    
    // 在运行时获取
    if let Ok(path) = std::env::var("PATH") {
        println!("PATH: {}", path);
    }

22. 不安全 Rust(Unsafe Rust)

当 Rust 的安全检查无法表达某些底层操作时,unsafe 关键字允许你绕过编译器的检查。使用 unsafe 时,你必须保证代码的安全性,编译器信任你的判断。

  • 不安全块:在 unsafe 块中可以进行以下操作:

    • 解引用裸指针
    • 调用不安全函数或方法
    • 访问或修改可变静态变量
    • 实现不安全 trait
    let mut num = 5;
    let r1 = &num as *const i32;  // 裸指针
    let r2 = &mut num as *mut i32;
    
    unsafe {
        println!("r1 is: {}", *r1);  // 解引用裸指针
        println!("r2 is: {}", *r2);
    }
  • 不安全函数:使用 unsafe fn 声明,调用时必须在 unsafe 块中。

    unsafe fn dangerous() {
        // 实现细节...
    }
    
    unsafe {
        dangerous();  // 调用不安全函数
    }
  • 外部函数接口(FFI):与 C 语言库交互。

    extern "C" {
        fn abs(input: i32) -> i32;
    }
    
    fn main() {
        unsafe {
            println!("Absolute value of -3: {}", abs(-3));
        }
    }
  • 裸指针类型*const T(不可变)和 *mut T(可变)。

    • 可以跨越线程共享(不检查 Send/Sync)
    • 可以为空(使用 std::ptr::null()std::ptr::null_mut()
    • 解引用必须在 unsafe 块中进行
    let mut value = 42;
    let ptr = &mut value as *mut i32;
    
    let mutable_ref = unsafe { &mut *ptr };
    *mutable_ref = 100;
  • 联合体(Union):使用 union 声明,所有字段共享同一内存空间。

    #[repr(C)]
    union IntOrFloat {
        as_int: i32,
        as_float: f32,
    }
    
    fn main() {
        let mut u = IntOrFloat { as_int: 42 };
        unsafe {
            println!("as int: {}", u.as_int);
            println!("as float: {}", u.as_float);
        }
    }

常见场景与注意事项:

  • 尽量将 unsafe 代码隔离在小型、独立的函数或模块中

  • 使用 unsafe 时务必编写充分的注释说明为何安全

  • 结合 unsafe 代码与安全抽象(如 Vec<T> 内部使用 unsafe 但对外提供安全 API`)

  • 使用 core::ptr::writecore::ptr::read 等函数时注意别名的规则(strict provenance)

  • CStr 与 CString:在 FFI 中安全地传递 C 字符串。

    use std::ffi::{CStr, CString};
    
    extern "C" {
        fn puts(s: *const libc::c_char);
    }
    
    fn main() {
        let cstring = CString::new("Hello from Rust!").unwrap();
        unsafe {
            puts(cstring.as_ptr());
        }
    }

23. 常用 trait 详解

Rust 标准库定义了许多重要的 trait,理解它们对于编写惯用代码至关重要。

  • Deref trait:允许自定义解引用行为。

    use std::ops::Deref;
    
    struct MyBox<T>(T);
    
    impl<T> Deref for MyBox<T> {
        type Target = T;
        
        fn deref(&self) -> &Self::Target {
            &self.0
        }
    }
    
    let x = 5;
    let y = MyBox::new(x);
    assert_eq!(5, *y);  // 自动解引用
    • Deref 强制转换:当类型实现了 Deref,值引用会自动转换为内部类型的引用
    • 常用于智能指针(如 Box<T>Rc<T>Arc<T>
  • Drop trait:自定义值被移出作用域时的行为(资源清理)。

    struct File {
        name: String,
    }
    
    impl Drop for File {
        fn drop(&mut self) {
            println!("Dropping file: {}", self.name);
        }
    }
    
    fn main() {
        let _f = File { name: "data.txt".to_string() };
        // 当离开作用域时,drop 被调用
    }
    • 字段按声明顺序逆序 drop
    • 不能手动调用 drop,应使用 std::mem::drop 函数
  • Default trait:为类型提供默认值。

    #[derive(Default)]
    struct Config {
        host: String,
        port: u16,
    }
    
    fn main() {
        let config = Config::default();
        // 或使用结构体更新语法
        let config = Config { 
            port: 8080, 
            ..Default::default() 
        };
    }
  • From 和 Into trait:类型转换。

    #[derive(Debug)]
    struct Point { x: i32, y: i32 }
    
    impl From<(i32, i32)> for Point {
        fn from(coords: (i32, i32)) -> Self {
            Point { x: coords.0, y: coords.1 }
        }
    }
    
    fn main() {
        let p = Point::from((10, 20));
        let p: Point = (30, 40).into();
    }
  • AsRef 和 AsMut trait:借用转换。

    fn process_path<P: AsRef<std::path::Path>>(path: P) {
        let path = path.as_ref();
        // 使用 path...
    }
    
    fn main() {
        process_path("/home/user");  // &str 实现 AsRef<Path>
        process_path(std::path::Path::new("data.txt"));
    }
  • ToString 和 FromStr trait:字符串转换。

    use std::str::FromStr;
    
    #[derive(Debug, PartialEq)]
    struct Point { x: i32, y: i32 }
    
    impl FromStr for Point {
        type Err = String;
        
        fn from_str(s: &str) -> Result<Self, Self::Err> {
            let coords: Vec<i32> = s
                .split(',')
                .map(|n| n.parse().map_err(|_| "Invalid number".to_string()))
                .collect::<Result<_, _>>()?;
            
            match coords.as_slice() {
                [x, y] => Ok(Point { x: *x, y: *y }),
                _ => Err("Expected exactly two numbers".to_string()),
            }
        }
    }
    
    fn main() {
        let p = "10,20".parse::<Point>().unwrap();
        println!("{} {:?}", p.to_string(), p);
    }

24. 线程本地存储与同步原语

  • 线程本地存储(Thread Local Storage):使用 thread_local! 宏声明。

    use std::cell::RefCell;
    use std::thread;
    
    thread_local! {
        static THREAD_COUNTER: RefCell<u32> = RefCell::new(0);
    }
    
    fn main() {
        THREAD_COUNTER.with(|c| *c.borrow_mut() = 10);
        
        let handles: Vec<_> = (0..4).map(|_| {
            thread::spawn(|| {
                THREAD_COUNTER.with(|c| {
                    let mut counter = c.borrow_mut();
                    *counter += 1;
                    println!("Thread counter: {}", *counter);
                })
            })
        }).collect();
        
        for h in handles {
            h.join().unwrap();
        }
    }
  • OnceLock 与 OnceCell(Rust 1.70+):单次初始化的线程安全容器。

    use std::sync::OnceLock;
    
    static CONFIG: OnceLock<Config> = OnceLock::new();
    
    #[derive(Debug)]
    struct Config { port: u16, workers: usize }
    
    fn get_config() -> &'static Config {
        CONFIG.get_or_init(|| {
            Config {
                port: std::env::var("PORT").unwrap_or_default().parse().unwrap_or(8080),
                workers: std::thread::available_parallelism().unwrap().get(),
            }
        })
    }
    
    fn main() {
        let config = get_config();
        println!("Port: {}, Workers: {}", config.port, config.workers);
    }
    • OnceLock:用于 static 声明的线程安全单次初始化
    • OnceCell:非线程安全版本,适用于单线程
    • get_or_init:首次访问时初始化
    • set:尝试设置值,返回 Result
    • get_or_try_init:支持返回 Result 的初始化
  • Barrier:使多个线程在某个点上同步。

    use std::sync::{Arc, Barrier};
    use std::thread;
    
    let barrier = Arc::new(Barrier::new(4));
    let mut handles = vec![];
    
    for _ in 0..4 {
        let barrier = Arc::clone(&barrier);
        handles.push(thread::spawn(move || {
            println!("before barrier");
            barrier.wait();  // 所有线程到达这里后继续
            println!("after barrier");
        }));
    }
    
    for h in handles {
        h.join().unwrap();
    }
  • Scope 线程:在指定作用域内生成线程,可以安全地借用局部变量。

    use std::thread;
    
    fn main() {
        let mut numbers = vec![1, 2, 3, 4];
        
        thread::scope(|s| {
            // 在作用域内生成线程,可以借用 numbers
            s.spawn(|| {
                println!("Numbers: {:?}", numbers);
            });
            
            s.spawn(|| {
                numbers.push(5);
            });
        });
        // 所有作用域线程完成后,numbers 再次可变
        numbers.push(6);
    }

25. 常用外部 crate

Rust 生态系统丰富,以下是各领域常用的 crate。

  • 命令行参数解析

    // 使用 clap(功能完整,最流行)
    use clap::{Parser, ArgAction};
    
    #[derive(Parser, Debug)]
    #[command(name = "myapp")]
    struct Args {
        #[arg(short, long, default_value = "localhost")]
        host: String,
        
        #[arg(short, long, default_value_t = 8080)]
        port: u16,
        
        #[arg(action = ArgAction::SetTrue)]
        verbose: bool,
    }
    
    fn main() {
        let args = Args::parse();
        println!("{:?}", args);
    }
    • clap:功能完整,支持子命令、配置文件、自动帮助信息
    • argh:轻量级,零依赖,适合简单场景
    • structopt(已合并到 clap):基于结构体的声明式解析
  • 序列化与反序列化

    // serde - 序列化框架
    use serde::{Serialize, Deserialize};
    
    #[derive(Serialize, Deserialize, Debug)]
    struct User {
        id: u64,
        username: String,
        active: bool,
    }
    
    fn main() -> Result<(), serde_json::Error> {
        let user = User {
            id: 1,
            username: "alice".to_string(),
            active: true,
        };
        
        // 序列化为 JSON
        let json = serde_json::to_string(&user)?;
        println!("JSON: {}", json);
        
        // 反序列化
        let parsed: User = serde_json::from_str(&json)?;
        println!("{:?}", parsed);
        Ok(())
    }
    • serde:序列化框架,支持 JSON、YAML、TOML、MessagePack 等
    • serde_json:JSON 支持
    • toml:TOML 配置文件解析
    • yaml-rust:YAML 支持
  • 日志记录

    // 使用 log + env_logger
    use log::{info, warn, error};
    
    fn main() {
        env_logger::init();
        
        info!("Starting application");
        warn!("Configuration file not found, using defaults");
        error!("Failed to connect to database");
        
        // 格式化日志
        info!("User {} logged in at {}:{:?}", "alice", "10:00", [1, 2, 3]);
    }
    • log:日志 facade(接口)
    • env_logger:基于环境变量配置的实现
    • tracing:更先进的分布式追踪框架,支持 span 和事件
  • 随机数生成

    use rand::{Rng, thread_rng, random};
    
    fn main() {
        let mut rng = thread_rng();
        
        // 范围随机数
        let n: u32 = rng.gen_range(1..100);
        
        // 浮点数随机数
        let f: f64 = rng.gen_range(0.0..1.0);
        
        // 随机布尔值
        let b: bool = random();
        
        // 从集合中随机选择
        let choices = ["apple", "banana", "cherry"];
        let choice = choices[rng.gen_range(0..choices.len())];
        
        println!("n={}, f={}, b={}, choice={}", n, f, b, choice);
    }
  • 日期与时间处理

    use chrono::{Local, DateTime, Utc};
    
    fn main() {
        let now = Local::now();
        println!("Local time: {}", now);
        
        let utc: DateTime<Utc> = Utc::now();
        println!("UTC time: {}", utc);
        
        // 格式化
        println!("Formatted: {}", now.format("%Y-%m-%d %H:%M:%S"));
        
        // 解析
        let parsed = DateTime::parse_from_rfc3339("2024-01-15T10:30:00Z").unwrap();
        println!("Parsed: {}", parsed);
        
        // 时区转换
        let tokyo = now.with_timezone(&chrono::FixedOffset::east(9 * 3600));
        println!("Tokyo: {}", tokyo);
    }
  • 正则表达式

    use regex::Regex;
    
    fn main() {
        let re = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap();
        
        let date = "2024-01-15";
        if let Some(caps) = re.captures(date) {
            println!("Year: {}, Month: {}, Day: {}",
                caps.get(1).unwrap().as_str(),
                caps.get(2).unwrap().as_str(),
                caps.get(3).unwrap().as_str());
        }
        
        // 替换
        let re2 = Regex::new(r"\s+").unwrap();
        let result = re2.replace_all("a b   c", "-");
        println!("Replaced: {}", result);  // "a-b-c"
        
        // 迭代匹配
        let text = "foo1, foo2, foo3";
        for mat in re.find_iter(text) {
            println!("Found: {}", mat.as_str());
        }
    }
  • HTTP 客户端与服务端

    // reqwest - HTTP 客户端
    use reqwest;
    
    #[tokio::main]
    async fn main() -> Result<(), reqwest::Error> {
        let response = reqwest::Client::new()
            .get("https://httpbin.org/ip")
            .send()
            .await?;
        
        let body = response.text().await?;
        println!("Response: {}", body);
        Ok(())
    }
    // actix-web - HTTP 服务端
    use actix_web::{web, App, HttpServer, Responder};
    
    async fn index() -> impl Responder {
        "Hello, World!"
    }
    
    #[tokio::main]
    async fn main() -> std::io::Result<()> {
        HttpServer::new(|| {
            App::new()
                .route("/", web::get().to(index))
        })
        .bind("127.0.0.1:8080")?
        .run()
        .await
    }

26. 惯用模式与最佳实践

  • RAII(资源获取即初始化):利用作用域自动释放资源。

    struct DatabaseConnection {
        conn: *mut libc::c_void,
    }
    
    impl DatabaseConnection {
        fn connect() -> Result<Self, &'static str> {
            // 模拟连接...
            Ok(DatabaseConnection { conn: std::ptr::null_mut() })
        }
    }
    
    impl Drop for DatabaseConnection {
        fn drop(&mut self) {
            println!("Closing database connection");
            // 实际关闭连接...
        }
    }
    
    fn process_data() {
        let db = DatabaseConnection::connect().unwrap();
        // 使用数据库...
        // 当函数结束时,drop 被自动调用
    }
  • Builder 模式:用于复杂对象的构造。

    struct UserBuilder {
        name: String,
        email: Option<String>,
        age: Option<u32>,
        active: bool,
    }
    
    impl UserBuilder {
        fn new(name: &str) -> Self {
            UserBuilder {
                name: name.to_string(),
                email: None,
                age: None,
                active: true,
            }
        }
        
        fn email(mut self, email: &str) -> Self {
            self.email = Some(email.to_string());
            self
        }
        
        fn age(mut self, age: u32) -> Self {
            self.age = Some(age);
            self
        }
        
        fn build(self) -> Result<User, &'static str> {
            let email = self.email.ok_or("Email is required")?;
            Ok(User {
                name: self.name,
                email,
                age: self.age,
                active: self.active,
            })
        }
    }
    
    #[derive(Debug)]
    struct User {
        name: String,
        email: String,
        age: Option<u32>,
        active: bool,
    }
    
    fn main() {
        let user = UserBuilder::new("alice")
            .email("alice@example.com")
            .age(30)
            .build()
            .unwrap();
        
        println!("{:?}", user);
    }
  • Newtype 模式:类型安全的封装。

    struct UserId(u32);
    struct SessionId(u32);
    
    fn authenticate(user_id: UserId) {
        println!("Authenticating user {}", user_id.0);
    }
    
    fn create_session(session_id: SessionId) {
        println!("Creating session {}", session_id.0);
    }
    
    fn main() {
        let uid = UserId(123);
        let sid = SessionId(456);
        
        authenticate(uid);
        create_session(sid);
        
        // 不能混淆 UserId 和 SessionId
        // authenticate(sid);  // 编译错误!
    }
  • 类型状态机:使用类型系统编码状态。

    // 状态标记
    struct Draft;
    struct Published;
    
    // 博文
    struct Post<S> {
        content: String,
        _state: std::marker::PhantomData<S>,
    }
    
    impl Post<Draft> {
        fn new() -> Self {
            Post {
                content: String::new(),
                _state: std::marker::PhantomData,
            }
        }
        
        fn add_text(&mut self, text: &str) {
            self.content.push_str(text);
        }
        
        fn request_review(self) -> Post<Published> {
            Post {
                content: self.content,
                _state: std::marker::PhantomData,
            }
        }
    }
    
    impl Post<Published> {
        fn content(&self) -> &str {
            &self.content
        }
    }
    
    fn main() {
        let mut post = Post::<Draft>::new();
        post.add_text("Hello");
        
        let post = post.request_review();
        println!("Published: {}", post.content());
        
        // post.add_text("More");  // 编译错误!已发布不能修改
    }
  • 错误类型封装Result 中封装多种错误类型。

    use std::fmt;
    
    #[derive(Debug)]
    enum MyError {
        Io(std::io::Error),
        Parse(std::num::ParseIntError),
        Custom(String),
    }
    
    impl fmt::Display for MyError {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match self {
                MyError::Io(e) => write!(f, "IO error: {}", e),
                MyError::Parse(e) => write!(f, "Parse error: {}", e),
                MyError::Custom(s) => write!(f, "Error: {}", s),
            }
        }
    }
    
    impl std::error::Error for MyError {}
    
    impl From<std::io::Error> for MyError {
        fn from(e: std::io::Error) -> Self {
            MyError::Io(e)
        }
    }
    
    impl From<std::num::ParseIntError> for MyError {
        fn from(e: std::num::ParseIntError) -> Self {
            MyError::Parse(e)
        }
    }
    
    fn read_and_parse(path: &str) -> Result<i32, MyError> {
        let content = std::fs::read_to_string(path)?;
        let num = content.trim().parse()?;
        Ok(num)
    }

27. Panic 与错误恢复

  • Panic 行为

    // unwrap - 提取值或 panic
    let opt: Option<i32> = Some(5);
    let val = opt.unwrap();  // 5
    // let val = None.unwrap();  // panic!
    
    // expect - 带消息的 unwrap
    let val = opt.expect("Expected a value");
    
    // unwrap_or - 提供默认值
    let val = opt.unwrap_or(0);  // 5
    
    // unwrap_or_else - 提供计算默认值的闭包
    let val = opt.unwrap_or_else(|| 10 * 2);  // 5
    
    // unwrap_or_default - 使用类型的 Default
    let opt_str: Option<String> = None;
    let val = opt_str.unwrap_or_default();  // ""
  • Panic 时中止

    // Cargo.toml
    [profile.release]
    panic = "abort"  // 减小二进制体积,不展开栈
    
    // 或在代码中
    std::panic::set_hook(Box::new(|info| {
        println!("Panic: {}", info);
        std::process::exit(1);
    }));
  • 捕获 Panicstd::panic::catch_unwind

    fn may_panic() -> i32 {
        if rand::random() {
            panic!("Oops!");
        }
        42
    }
    
    fn main() {
        let result = std::panic::catch_unwind(|| {
            may_panic()
        });
        
        match result {
            Ok(value) => println!("Success: {}", value),
            Err(_) => println!("Caught panic"),
        }
    }
    • 注意:catch_unwind 只能捕获展开(unwinding)的 panic,不能捕获 panic = "abort"abort 发出的 panic
  • Assert 宏

    // debug 断言(只在 debug 构建时检查)
    debug_assert!(x > 0);
    
    // release 断言(始终检查)
    assert!(x > 0, "x must be positive, got {}", x);
    
    // 相等/不等断言
    assert_eq!(a, b);
    assert_ne!(a, b);

28. Cargo 工作空间与依赖管理

  • 工作空间(Workspace)

    # Cargo.toml(根目录)
    [workspace]
    members = [
        "crates/backend",
        "crates/cli",
        "crates/common",
    ]
    resolver = "2"  // 锁定依赖解析规则
    
    # 共享依赖
    [workspace.dependencies]
    tokio = { version = "1.0", features = ["full"] }
    serde = { version = "1.0", features = ["derive"] }
    # crates/backend/Cargo.toml
    [package]
    name = "backend"
    version.workspace = true
    edition.workspace = true
    
    [dependencies]
    tokio = { workspace = true }
    serde = { workspace = true }
  • 依赖特性(Features)

    [features]
    default = ["std", "logging"]
    std = []
    logging = ["dep:log"]
    ssl = ["dep:openssl"]
    
    [dependencies]
    log = { optional = true }
    openssl = { optional = true }
  • 条件编译

    #[cfg(feature = "ssl")]
    mod ssl {
        // SSL 相关代码...
    }
    
    #[cfg(feature = "logging")]
    fn setup_logging() {
        // 设置日志...
    }
  • 开发依赖

    [dev-dependencies]
    tempfile = "3.0"
    assert_fs = "1.0"
    proptest = "1.0"
  • 构建脚本

    // build.rs
    fn main() {
        // 通知 Cargo 重新运行的条件
        println!("cargo:rerun-if-changed=build.rs");
        println!("cargo:rerun-if-changed=src/bindings/*");
        
        // 设置环境变量供依赖使用
        println!("cargo:rustc-cfg=feature=\"my_feature\"");
        
        // 编译 C 库
        cc::Build::new()
            .file("src/native/lib.c")
            .compile("mylib");
    }

29. 性能优化技巧

  • 减少分配

    // 预分配容量
    let mut vec = Vec::with_capacity(1000);
    
    // 使用栈分配的数组(如果大小已知)
    let mut arr = [0i32; 1024];
    
    // 避免字符串连接时的中间分配
    let mut s = String::with_capacity(100);
    s.push_str("Hello");
    s.push(' ');
    s.push_str("World");
  • 内联与优化提示

    #[inline]
    fn small_function(x: i32) -> i32 {
        x + 1
    }
    
    #[inline(always)]
    fn tiny_function() -> i32 { 42 }
    
    #[cold]
    fn unlikely_path() {
        // 不太可能执行的代码...
    }
    
    #[track_caller]
    fn assert_helper(condition: bool, msg: &str) {
        if !condition {
            panic!("{}", msg);
        }
    }
  • 迭代器优化

    // 使用迭代器适配器(零成本抽象)
    let sum: i32 = (0..1000)
        .filter(|&x| x % 2 == 0)
        .map(|x| x * x)
        .sum();
    
    // 使用预估容量
    let v: Vec<_> = iter.collect_with_capacity_hint(size_hint);
  • 内存布局优化

    // 使用 repr(C) 或 repr(packed) 调整内存布局
    #[repr(C)]
    struct PackedData {
        a: u8,
        // b: u32,  // 如果不打包,可能会有填充
    }
    
    #[repr(packed)]
    struct NoPadding {
        a: u8,
        b: u32,
    }
    
    // 使用 Option 优化
    #[repr(u8)]
    enum MyEnum {
        A = 0,
        B = 1,
    }
  • 并行与并发优化

    use rayon::prelude::*;
    
    fn parallel_sum(data: &[i32]) -> i32 {
        data.par_iter().sum()
    }
    
    fn parallel_map(data: Vec<i32>) -> Vec<i32> {
        data.into_par_iter()
            .map(|x| x * 2)
            .collect()
    }
  • 避免克隆

    // 不好:每次迭代都克隆
    for s in strings.iter() {
        let owned = s.clone();  // 克隆
        process(owned);
    }
    
    // 好:借用
    for s in strings.iter() {
        process(s);  // 借用
    }
    
    // 好:使用引用计数共享
    use std::sync::Arc;
    let shared = Arc::new(data);
    let handles: Vec<_> = (0..4).map(|_| {
        let data = Arc::clone(&shared);
        std::thread::spawn(move || process(&data))
    }).collect();

30. 常见陷阱与调试技巧

  • 闭包捕获陷阱

    let mut funcs: Vec<fn() -> i32> = Vec::new();
    for i in 0..5 {
        funcs.push(|| i * 2);  // 错误!闭包捕获了 i
    }
    
    // 解决方案:复制值
    for i in 0..5 {
        funcs.push(move || i * 2);
    }
  • 循环引用陷阱

    use std::rc::Rc;
    use std::cell::RefCell;
    
    struct Node {
        value: i32,
        next: RefCell<Option<Rc<Node>>>,
    }
    
    let a = Rc::new(Node { value: 1, next: RefCell::new(None) });
    let b = Rc::new(Node { value: 2, next: RefCell::new(None) });
    
    // 创建循环引用 - 内存泄漏!
    a.next.borrow_mut().replace(Rc::clone(&b));
    b.next.borrow_mut().replace(Rc::clone(&a));
    
    // 使用 Weak 打破循环
    struct NodeWithWeak {
        value: i32,
        next: RefCell<Option<Rc<NodeWithWeak>>>,
        prev: RefCell<Option<std::rc::Weak<NodeWithWeak>>>,
    }
  • 借用检查器陷阱

    let mut v = vec![1, 2, 3];
    
    // 不好:在借用期间修改
    // let first = &v[0];
    // v.push(4);  // 错误!可能重新分配
    
    // 好:先获取引用,再修改
    let first = v[0];
    v.push(4);
    
    // 好:使用索引代替引用
    let idx = 0;
    v.push(v[idx] * 2);
  • 迭代器消费陷阱

    let v = vec![1, 2, 3];
    
    // into_iter 消费所有权
    for x in v.into_iter() {
        println!("{}", x);
    }
    // println!("{:?}", v);  // 错误!v 已被消费
    
    // iter 借用
    let v = vec![1, 2, 3];
    for x in v.iter() {
        println!("{}", x);
    }
    println!("{:?}", v);  // 正常!v 仍可使用
    
    // iter_mut 可变借用
    for x in v.iter_mut() {
        *x *= 2;
    }
  • 调试技巧

    // 使用 dbg! 宏打印调试信息
    let x = 5;
    let y = dbg!(x + 1);  // 打印: [src/main.rs:3] x + 1 = 6
    
    // 打印类型
    let x = 5;
    println!("{:?}", std::any::type_name_of_val(&x));  // "i32"
    
    // 使用 Miri 检测未定义行为
    // cargo +nightly install miri
    // cargo miri run
    
    // 使用 clippy 获取建议
    // cargo clippy

23. 资源推荐