Skip to content

Rust 速查表

专注于安全性、速度和并发性的系统级语言。

01

基础

变量与可变性

Rust 变量默认是不可变的——这是防止意外修改的核心安全特性。仅在你确实需要修改值时才使用 'mut'。'const' 需要显式类型并在编译时内联,而 'static' 具有固定的内存地址和 'static 生命周期。

rust
let x = 5;            // immutable by default
let mut y = 10;        // mutable with 'mut'
y += 1;                // OK, y is mutable

const MAX: u32 = 100;  // compile-time constant
static GREETING: &str = "Hi"; // global static

let z: i32 = -5;       // explicit type annotation

遮蔽

遮蔽允许你重用变量名同时改变其类型或值。与 'mut' 不同,遮蔽会创建一个新的绑定——适用于在不发明新名称的情况下转换数据(例如,将字符串解析为整数)。旧值在新绑定后会被丢弃。

rust
let x = 5;
let x = x * 2;         // shadow previous x, now 10
let x = "text";        // can even change type!
println!("{}", x);     // "text"

// shadowing vs mut: shadowing creates a NEW variable
// with the same name, allowing type changes

注释与打印

使用 /// 编写条目文档(通过 'cargo doc' 显示),使用 //! 编写模块/crate 文档。println! 写入 stdout,eprintln! 写入 stderr。{} 占位符支持格式化规范:> 表示右对齐,< 表示左对齐,^ 表示居中,.N 表示精度,#b/#o/#x 表示二进制/八进制/十六进制。

rust
// Line comment
/* Block comment */
/// Doc comment (renders in rustdoc)
//! Module-level doc comment

let name = "Alice";
println!("Hello, {}!", name);        // format string
println!("{0} {1} {0}", "a", "b");   // positional args
println!("{:>5}", 42);               // right-align, width 5
println!("{:.2}", 3.14159);          // 2 decimal places: 3.14
println!("{:#b}", 0b1010);           // binary: 0b1010
eprintln!("Error to stderr");        // error output

数据类型概述

Rust 没有隐式类型转换——使用 'as' 进行转换。i32 是默认整数类型,f64 是默认浮点类型。usize 用于索引和大小。char 是完整的 Unicode 标量值(不是字节),所以 '🦀' 是单个 char。

rust
// Scalar types
let a: i32 = 42;          // signed 32-bit integer
let b: u64 = 100;         // unsigned 64-bit
let c: f64 = 3.14;        // 64-bit float (default)
let d: bool = true;
let e: char = '🦀';        // 4-byte Unicode scalar

// isize/usize: pointer-sized (32 or 64 bit depending on arch)
let len: usize = vec![1,2,3].len();

// Compound types
let pair: (i32, &str) = (1, "hello");
let arr: [i32; 3] = [0; 3];   // [0, 0, 0]

类型转换与别名

'as' 转换是不检查的,可能会丢失数据(例如,300u16 as u8 会回绕到 44)。对于安全转换,使用 From/Into trait,它们为无损转换实现。类型别名可以提高可读性而不创建新类型——使用 'struct NewType(i32)' 实现独特的新类型模式。

rust
// 'as' keyword for primitive casts (may truncate)
let x: i32 = 42;
let y: f64 = x as f64;        // 42.0
let z: u8 = 300u16 as u8;     // truncated: 44

// From/Into traits for safe conversion
let s = String::from("hi");
let owned: String = "hi".into();

// Type aliases
type Kilometers = i32;
let distance: Kilometers = 5;
02

字符串

String 与 &str

String(拥有、堆)和 &str(借用切片)之间的区别在 Rust 中是基础性的。当你需要拥有/修改/增长文本时使用 String;当你只需要读取时使用 &str。&str 可以指向 String 的缓冲区或二进制文件中的字符串字面量。在函数参数中优先使用 &str 以获得灵活性。

rust
// &str: immutable string slice (borrowed)
let s1: &str = "literal";           // stored in binary
let s2: &str = &s3[..2];            // slice of a String

// String: owned, growable, heap-allocated
let mut s3: String = String::from("hello");
s3.push_str(", world");             // append
s3.push('!');                       // append char
s3.insert(0, '>');                  // insert at index
println!("{}", s3);                 // >hello, world!

// Convert: &str -> String
let owned = "literal".to_string();
let owned2 = String::from("literal");

字符串方法

字符串方法返回新的拥有 String 而不是原地修改(可变 String 上的 push_*/insert 方法除外)。split() 返回迭代器,所以使用 .collect() 来物化。注意,字符串上不允许索引 s[0],因为 UTF-8 边界与字节索引不对齐——改用 s.chars().nth(0)。

rust
let s = String::from("Hello, World");

// inspection
println!("len: {}", s.len());              // 12
println!("is_empty: {}", s.is_empty());    // false
println!("contains 'World': {}", s.contains("World")); // true
println!("starts_with 'Hello': {}", s.starts_with("Hello")); // true

// transformation
let upper = s.to_uppercase();              // HELLO, WORLD
let replaced = s.replace("o", "0");        // Hell0, W0rld
let trimmed = "  hi  ".trim().to_string(); // hi

// splitting
for word in s.split(", ") {
    println!("{}", word); // Hello / World
}
let parts: Vec<&str> = s.split_whitespace().collect();

格式化与拼接

+ 运算符获取左侧 String 的所有权并借用右侧(&str)。这就是为什么 s1 + &s2 后 s1 变得无效。对于拼接多个字符串,优先使用 format!,它更易读且不会移动任何操作数。concat! 仅适用于字面量并产生 &'static str。

rust
// format! macro creates a new String
let name = "Alice";
let msg = format!("Hi {}, you have {} messages", name, 5);

// concatenation
let s1 = String::from("Hello");
let s2 = String::from(" World");
let s3 = s1 + &s2;        // s1 moved, s2 borrowed
// s1 is now invalid!

let s4 = format!("{}{}", s2, s3); // neither moved

// concat! macro for literals
let s5 = concat!("foo", "bar"); // "foobar" at compile time

遍历字符与字节

Rust 字符串是 UTF-8 编码的,所以字节索引 != 字符索引。chars() 遍历 Unicode 标量值(O(n) 解码),bytes() 遍历原始字节。使用 [n..m] 切片时,如果 n 或 m 落在多字节字符中间会 panic。对于字节级访问,通过 as_bytes() 转换为 Vec<u8>。

rust
let s = "héllo";  // é is 2 bytes in UTF-8

// iterate over chars (Unicode scalar values)
for c in s.chars() {
    print!("{} ", c);  // h é l l o
}

// iterate over bytes
for b in s.bytes() {
    print!("{} ", b);  // 104 195 169 108 108 111
}

// get char at index (O(n) — must walk UTF-8)
let third = s.chars().nth(2); // Some('l')

// string slices must be on char boundaries
let slice = &s[0..1]; // "h" — OK
// let bad = &s[0..2]; // PANIC if mid-é!

解析与转换

parse() 返回 Result,因为字符串可能不是有效数字——始终处理错误。turbofish 语法 parse::<T>() 允许你内联指定类型。将 String 转换为 &str 是免费的(只是借用),但 &str 转换为 String 需要分配内存。collect() 可以从字符迭代器构建 String。

rust
// String/str -> number
let n: i32 = "42".parse().unwrap();
let n2 = "42".parse::<i32>().unwrap(); // turbofish syntax
let f: f64 = "3.14".parse().unwrap();

// number -> String
let s = 42.to_string();
let s2 = format!("{}", 42);

// String -> &str (free, just dereference)
let owned = String::from("hi");
let borrowed: &str = &owned;

// collect chars into String
let upper: String = "hello".chars().map(|c| c.to_uppercase().next().unwrap()).collect();
03

数据结构

数组与切片

数组 [T; N] 在编译时已知固定大小并存在于栈上。切片 &[T] 是胖指针(指针 + 长度),借用一段连续序列——它们允许函数接受任何数组或向量而不关心大小。在函数签名中使用切片以获得通用性。

rust
// Fixed-size array (stack allocated)
let arr: [i32; 3] = [1, 2, 3];
let zeros = [0; 5];           // [0, 0, 0, 0, 0]
println!("first: {}", arr[0]);
println!("len: {}", arr.len());

// Slice: a view into an array/vector
let slice: &[i32] = &arr[1..3];  // [2, 3]
let full: &[i32] = &arr;          // whole array
let first = &arr[..1];            // [1]

// iterating
for n in &arr {
    println!("{}", n);
}

向量(Vec<T>)

Vec<T> 是 Rust 的可增长数组,由堆内存支持,容量加倍。push/pop 是 O(1) 摊销;insert/remove 是 O(n),因为元素需要移动。当你想要安全访问时使用 .get(i) 而不是 v[i](返回 Option)。into_iter() 消费向量,产生拥有的值。

rust
// growable heap array
let mut v: Vec<i32> = Vec::new();
let v2 = vec![1, 2, 3];          // macro shorthand

v.push(4);                        // append
v.pop();                          // remove last -> Option
v.insert(0, 0);                   // insert at index (O(n))
v.remove(0);                      // remove at index (O(n))
v.extend([5, 6]);                 // append multiple

// access
println!("{}", v[0]);             // panics if out of bounds
println!("{:?}", v.get(0));       // Some(&4) — safe

// iterate by value, ref, or mut ref
for n in &v { print!("{}", n); }
for n in &mut v { *n *= 2; }      // double each
let owned: Vec<i32> = v.into_iter().collect();

HashMap 与 BTreeMap

HashMap 使用哈希实现 O(1) 平均访问但没有排序。BTreeMap 使用 B 树实现 O(log n) 访问但保持键排序。当你需要快速查找时使用 HashMap;当你需要有序迭代或范围查询时使用 BTreeMap。entry().or_insert() 是 'upsert' 的惯用方式——它返回值的可变引用,如果不存在则插入默认值。

rust
use std::collections::HashMap;
use std::collections::BTreeMap;

// HashMap: O(1) average lookup, unordered
let mut scores: HashMap<String, i32> = HashMap::new();
scores.insert(String::from("Alice"), 10);
scores.entry("Bob".into()).or_insert(0); // insert if absent
let alice = scores.get("Alice"); // Some(&10)

// iterate (unordered)
for (name, score) in &scores {
    println!("{}: {}", name, score);
}

// BTreeMap: O(log n) lookup, sorted by key
let mut bt: BTreeMap<String, i32> = BTreeMap::new();
bt.insert("zebra".into(), 1);
bt.insert("apple".into(), 2);
// iterates in sorted order: apple, zebra

HashSet 与 BTreeSet

HashSet 存储唯一值,O(1) 成员检查。集合操作(并集、交集、差集、对称差集)返回迭代器。使用集合进行去重、成员测试和数学集合操作。BTreeSet 是由 BTreeMap 支持的排序等价物。

rust
use std::collections::HashSet;

let mut a: HashSet<i32> = [1, 2, 3].into_iter().collect();
let b: HashSet<i32> = [3, 4, 5].into_iter().collect();

a.insert(4);
a.remove(&1);
println!("contains 2: {}", a.contains(&2));

// set operations
let union: HashSet<_> = a.union(&b).copied().collect();
let inter: HashSet<_> = a.intersection(&b).copied().collect();
let diff: HashSet<_> = a.difference(&b).copied().collect();
let sym: HashSet<_> = a.symmetric_difference(&b).copied().collect();

元组与解构

元组将固定数量的可能不同类型的值组合在一起。使用 .0、.1 等访问字段。使用 let 模式解构是惯用做法。单元类型 () 只有一个值 (),在其他语言中类似 void。元组通常用于从函数返回多个值。

rust
// tuples can hold different types
let tup: (i32, f64, &str) = (42, 3.14, "hi");

// access by index
println!("{} {} {}", tup.0, tup.1, tup.2);

// destructuring
let (n, pi, s) = tup;
println!("{} {} {}", n, pi, s);

// nested
let ((a, b), c) = ((1, 2), 3);

// unit tuple (empty)
let unit: () = ();

// function returning multiple values
fn divmod(a: i32, b: i32) -> (i32, i32) {
    (a / b, a % b)
}
let (q, r) = divmod(17, 5); // (3, 2)
04

控制流

If / Else If / Else

与许多语言不同,Rust 中的 'if' 是一个返回值的表达式。这消除了对三元运算符(condition ? a : b)的需求——只需使用 if/else。两个分支必须返回相同的类型。条件不需要括号,但主体必须是块 { }。

rust
let score = 85;

// if is an expression — returns a value
let grade = if score >= 90 {
    "A"
} else if score >= 80 {
    "B"
} else if score >= 70 {
    "C"
} else {
    "F"
};
println!("Grade: {}", grade);

// arms must return same type
// let bad = if true { 1 } else { "two" }; // ERROR!

循环:loop、while、for

loop 创建无限循环——break 退出它并可以返回一个值(break value)。while 在每次迭代前检查条件。for 是最常见的循环,遍历范围、数组、向量、迭代器。使用 'continue' 跳到下一次迭代。范围:a..b 是排他的,a..=b 是包含的。

rust
// loop: infinite, use break to exit
let mut count = 0;
let result = loop {
    count += 1;
    if count == 10 {
        break count * 2; // break with value
    }
};
println!("{}", result); // 20

// while: condition-checked loop
let mut n = 5;
while n > 0 {
    println!("{}", n);
    n -= 1;
}

// for: iterate over anything with IntoIterator
for i in 0..5 { print!("{}", i); }      // 01234
for i in (1..=3).rev() { print!("{}", i); } // 321
for ch in "hello".chars() { print!("{}", ch); }

Match(模式匹配)

match 是 Rust 强大的模式匹配构造。它必须是穷尽的(覆盖所有可能性)——使用 _ 作为通配符。模式支持字面量、范围(..=)、或模式(|)、绑定和守卫(if)。match 是一个表达式并返回一个值。它是处理 Option 和 Result 等枚举的惯用方式。

rust
let coin = 25;
match coin {
    25 => println!("quarter"),
    10 => println!("dime"),
    5 => println!("nickel"),
    1 => println!("penny"),
    _ => println!("unknown"),  // catch-all (required)
}

// matching with bindings
let x = 3;
match x {
    1 | 2 => println!("one or two"),   // or-pattern
    3..=9 => println!("single digit"),  // range
    n if n % 2 == 0 => println!("even: {}", n), // guard
    _ => println!("other"),
}

// match is exhaustive — all cases must be covered

If Let 与 While Let

if let 是当你只关心一个变体时 match 的语法糖。它比 match 更简洁但不够穷尽——在其他情况不重要时使用。while let 在模式匹配时循环,通常与迭代器一起使用(next() 返回 Option)。else 分支是可选的。

rust
// if let: shorthand for matching one pattern
let maybe: Option<i32> = Some(5);

// verbose: match
match maybe {
    Some(x) => println!("got {}", x),
    None => println!("nothing"),
}

// concise: if let
if let Some(x) = maybe {
    println!("got {}", x);
} else {
    println!("nothing");
}

// while let: loop while pattern matches
let mut iter = vec![1, 2, 3].into_iter();
while let Some(n) = iter.next() {
    println!("{}", n);
}

Break、Continue 与标签

标签(单引号 + 名称)允许从嵌套循环中 break 或 continue 外层循环。当你需要一次退出多个循环层级时,这很重要。没有标签,break/continue 只影响最内层循环。标签是标志变量的干净替代方案。

rust
// continue: skip to next iteration
for i in 0..10 {
    if i % 2 == 0 { continue; }
    println!("{}", i); // prints odd numbers
}

// break: exit loop
for i in 0..10 {
    if i == 5 { break; }
    println!("{}", i); // prints 0..4
}

// labeled loops (for nested break/continue)
'outer: for i in 0..3 {
    for j in 0..3 {
        if i == 1 && j == 1 {
            break 'outer; // breaks the outer loop
        }
        println!("{} {}", i, j);
    }
}
05

函数与闭包

定义函数

函数使用 'fn' 关键字。没有分号的最后一个表达式是返回值(表达式)。添加分号会将其变成返回 () 的语句。仅在提前退出时使用显式 'return'。! 返回类型标记从不返回的发散函数(无限循环、panic、进程退出)。

rust
// basic function with return type
fn add(a: i32, b: i32) -> i32 {
    a + b  // no semicolon = expression = return value
}

// statements (with semicolon) return ()
fn greet(name: &str) {
    println!("Hi, {}", name);
    // implicit return ()
}

// explicit return
fn abs(x: i32) -> i32 {
    if x < 0 {
        return -x;  // early return needs 'return'
    }
    x  // tail expression
}

// diverging function (never returns)
fn forever() -> ! {
    loop {}
}

参数与实参

Rust 没有函数重载或可选参数(改用泛型或构建器)。仔细选择参数类型:&T 用于读访问,&mut T 用于写访问,T 用于所有权转移。切片(&[T])是接受可变长度序列的惯用方式。不支持默认参数——使用构建器模式或 Option<T>。

rust
// immutable borrow
fn len(s: &String) -> usize { s.len() }

// mutable borrow
fn push(v: &mut Vec<i32>) { v.push(42); }

// take ownership
fn consume(s: String) { println!("{}", s); }

// multiple return via tuple
fn swap(a: i32, b: i32) -> (i32, i32) { (b, a) }

// variadic-ish via slices
fn sum(nums: &[i32]) -> i32 {
    nums.iter().sum()
}
println!("{}", sum(&[1, 2, 3, 4])); // 10

闭包

闭包是可以捕获其环境的匿名函数。它们由用法推断。闭包默认通过引用捕获;'move' 强制所有权转移(对线程很重要)。闭包实现 Fn(借用)、FnMut(可变借用)或 FnOnce(消费)trait,使它们可以作为函数参数传递。

rust
// closure syntax: |params| body
let add = |a, b| a + b;
println!("{}", add(1, 2)); // 3

// type annotations (rarely needed)
let square = |x: i32| -> i32 { x * x };

// capturing environment
let multiplier = 3;
let multiply = |x| x * multiplier; // borrows multiplier
println!("{}", multiply(5)); // 15

// move closure: takes ownership of captured vars
let name = String::from("Alice");
let greet = move || println!("Hi {}", name);
// name is now moved into greet
greet();

高阶函数与迭代器

Rust 迭代器是惰性的——操作在调用 .collect() 或其他消费方法之前不会执行。这允许零成本抽象:编译器可以将链式迭代器方法优化为高效循环。常用方法:map(转换)、filter(选择)、fold(累积)、take(限制)、skip、enumerate、zip、flat_map。

rust
let nums = vec![1, 2, 3, 4, 5];

// map: transform each element
let doubled: Vec<i32> = nums.iter().map(|x| x * 2).collect();

// filter: keep elements matching predicate
let evens: Vec<&i32> = nums.iter().filter(|&&x| x % 2 == 0).collect();

// fold/reduce: accumulate
let sum: i32 = nums.iter().sum();              // 15
let product: i32 = nums.iter().product();       // 120
let combined = nums.iter().fold(0, |acc, x| acc + x);

// chain multiple operations (lazy!)
let result: Vec<i32> = nums.iter()
    .filter(|&&x| x > 1)
    .map(|&x| x * x)
    .collect(); // [4, 9, 16, 25]

函数指针与 Trait

fn(小写)是函数指针类型——零成本,但不能捕获环境。对于捕获的闭包,使用泛型 <F: Fn(...)> 约束。Fn 借用,FnMut 可变借用,FnOnce 消费。函数指针适用于在结构体中存储函数或传递给 C。带 Fn 约束的泛型更灵活,在单态化时仍然是零成本的。

rust
// function pointer type
type MathFn = fn(i32, i32) -> i32;

fn add(a: i32, b: i32) -> i32 { a + b }
fn mul(a: i32, b: i32) -> i32 { a * b }

fn apply(f: MathFn, a: i32, b: i32) -> i32 {
    f(a, b)
}
println!("{}", apply(add, 3, 4)); // 7
println!("{}", apply(mul, 3, 4)); // 12

// generic over Fn trait (accepts closures too)
fn apply_fn<F: Fn(i32, i32) -> i32>(f: F, a: i32, b: i32) -> i32 {
    f(a, b)
}
let closure = |a, b| a - b;
println!("{}", apply_fn(closure, 10, 3)); // 7
06

所有权与借用

所有权规则

所有权是 Rust 的核心内存管理系统——不需要垃圾回收器。当你赋值堆值(String、Vec)时,所有权移动,旧变量变得无效。栈类型(i32、f64、bool、char、Copy 类型的元组)实现 Copy 并被复制。这在编译时消除了 use-after-free 和 double-free 错误。

rust
// Rule 1: Each value has ONE owner
let s1 = String::from("hello");
let s2 = s1;  // s1's ownership MOVED to s2
// println!("{}", s1); // ERROR: s1 is invalid after move

// Rule 2: When owner goes out of scope, value is dropped
{
    let s = String::from("temp");
    // s is valid here
} // s is automatically dropped (memory freed)

// Rule 3: Copy types (i32, bool, char, etc.) are copied, not moved
let a = 5;
let b = a;  // a is copied, both valid
println!("{} {}", a, b); // OK

借用与引用

借用允许你使用值而不获取所有权。&T 创建不可变引用——你可以同时拥有多个。&mut T 创建可变引用——但只能有一个可变引用或任意数量的不可变引用,不能同时存在。这在编译时防止数据竞争。引用必须始终指向有效数据(没有悬空指针)。

rust
// &T: immutable borrow (read-only, multiple allowed)
fn calc_len(s: &String) -> usize {
    s.len()
    // s goes out of scope but is NOT dropped (we don't own it)
}
let s = String::from("hello");
let len = calc_len(&s);  // borrow s, don't move it
println!("'{}' has length {}", s, len); // s still valid

// &mut T: mutable borrow (exclusive, only ONE at a time)
fn push_world(s: &mut String) {
    s.push_str(", world");
}
let mut s2 = String::from("hello");
push_world(&mut s2);
println!("{}", s2); // hello, world

切片引用

切片是对集合连续部分的引用。它们是包含指针和长度的'胖指针'。字符串切片(&str)让函数接受 String 和字符串字面量。数组切片(&[T])适用于任何连续序列。切片借用底层数据,防止在切片存在时被修改或丢弃。

rust
// string slice: &str
let s = String::from("hello world");
let hello: &str = &s[0..5];   // "hello"
let world: &str = &s[6..];    // "world"
let full: &str = &s[..];      // "hello world"

// array slice: &[T]
let arr = [1, 2, 3, 4, 5];
let mid: &[i32] = &arr[1..4]; // [2, 3, 4]

// function accepting slices (idiomatic)
fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    for (i, &byte) in bytes.iter().enumerate() {
        if byte == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}

生命周期

生命周期告诉编译器引用有效多久。'a 注解不会改变生命周期——它描述关系。'static 是一个特殊的生命周期,持续整个程序(字符串字面量有它)。大多数代码使用生命周期省略(编译器推断)。当你需要显式生命周期时:函数返回引用,或结构体持有引用。

rust
// explicit lifetime annotation
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
// 'a means: the returned reference lives as long as
// the SHORTEST of x and y's lifetimes

let s1 = String::from("long string");
let s2 = String::from("hi");
let result = longest(s1.as_str(), s2.as_str());
println!("Longest: {}", result);

// struct holding references needs lifetime
struct Excerpt<'a> {
    part: &'a str,
}
let novel = String::from("call me Ishmael. years ago...");
let first_sentence = novel.split('.').next().unwrap();
let ex = Excerpt { part: first_sentence };

智能指针

Box<T> 将数据移到堆上(单一所有者)。Rc<T> 通过引用计数实现共享所有权(仅单线程)。Arc<T> 是使用原子操作的线程安全版本。RefCell<T> 将借用检查移到运行时,允许通过共享引用进行修改(内部可变性)。递归类型使用 Box,图状结构使用 Rc/Arc,需要修改共享数据时使用 RefCell。

rust
use std::rc::Rc;
use std::sync::Arc;
use std::cell::RefCell;

// Box<T>: heap allocation, single owner
let b = Box::new(5); // 5 lives on heap
println!("{}", b);   // dereferenced automatically

// Rc<T>: reference counting, multiple owners (single-threaded)
let a = Rc::new(String::from("shared"));
let b = Rc::clone(&a); // increments ref count
println!("count: {}", Rc::strong_count(&a)); // 2

// Arc<T>: atomic Rc, thread-safe
let arc = Arc::new(vec![1, 2, 3]);

// RefCell<T>: interior mutability (runtime borrow check)
let cell = RefCell::new(5);
*cell.borrow_mut() += 1; // mutable borrow checked at runtime
07

结构体、枚举与 Trait

定义结构体

结构体将相关字段组合在一起。命名字段结构体最常见。元组结构体在字段名无意义时有用(Color、Point)。单元结构体没有数据,用于实现 trait。.. 语法从另一个实例复制未指定的字段。结构体在栈上分配,除非它们包含堆类型(String、Vec、Box)。

rust
// named-field struct
struct User {
    name: String,
    age: u32,
    active: bool,
}

let u = User {
    name: String::from("Alice"),
    age: 30,
    active: true,
};

// field init shorthand
let name = String::from("Bob");
let u2 = User { name, age: 25, active: true };

// update syntax (copy remaining fields from another)
let u3 = User { age: 40, ..u2 };

// tuple struct
struct Color(u8, u8, u8);
let red = Color(255, 0, 0);

// unit struct (no fields, useful for traits)
struct AlwaysEqual;

使用 impl 的方法

方法放在 impl 块中。&self 不可变借用,&mut self 可变借用,self 获取所有权(消费)。关联函数(没有 self 参数)类似静态方法——用 Type::function() 调用。Self 是类型的别名。允许多个 impl 块,适用于按关注点拆分方法或条件编译。

rust
struct Rectangle {
    width: f64,
    height: f64,
}

impl Rectangle {
    // associated function (constructor, no &self)
    fn new(w: f64, h: f64) -> Self {
        Rectangle { width: w, height: h }
    }

    // method (borrows self)
    fn area(&self) -> f64 {
        self.width * self.height
    }

    // mutable method
    fn scale(&mut self, factor: f64) {
        self.width *= factor;
        self.height *= factor;
    }

    // consuming method (takes ownership)
    fn into_square(self) -> Rectangle {
        let side = (self.width + self.height) / 2.0;
        Rectangle { width: side, height: side }
    }
}

let mut r = Rectangle::new(10.0, 5.0);
println!("Area: {}", r.area()); // 50
r.scale(2.0);
let sq = r.into_square(); // r consumed

枚举与模式匹配

Rust 中的枚举是代数数据类型——每个变体可以携带不同的数据。这使它们比 C 枚举强大得多。使用 match 进行模式匹配可以解构变体并提取数据。当值可以是几种不同形式之一时使用枚举。matches! 宏是返回 bool 的单模式匹配简写。

rust
// enum with data (algebraic data type)
enum Message {
    Quit,                          // no data
    Move { x: i32, y: i32 },       // named fields
    Write(String),                 // tuple variant
    ChangeColor(i32, i32, i32),    // tuple variant
}

// pattern matching with destructuring
fn process(msg: Message) {
    match msg {
        Message::Quit => println!("Quit"),
        Message::Move { x, y } => println!("Move to ({}, {})", x, y),
        Message::Write(text) => println!("Write: {}", text),
        Message::ChangeColor(r, g, b) => println!("RGB({}, {}, {})", r, g, b),
    }
}

process(Message::Move { x: 10, y: 20 });
process(Message::Write(String::from("hello")));

// enums can have methods too
impl Message {
    fn is_quit(&self) -> bool {
        matches!(self, Message::Quit)
    }
}

Option 与 Result

Option<T> 替代 null——你必须显式处理 None 情况,消除了 NullPointerException 式的错误。Result<T, E> 用于可能失败的操作。两者都有丰富的方法:map(转换)、and_then(链式)、unwrap_or(默认值)、is_some/is_ok(检查)。Result 上的 ? 运算符自动传播错误。这两个类型是 Rust 错误处理的支柱。

rust
// Option<T>: Some(value) or None (replaces null)
fn find_user(id: i32) -> Option<String> {
    if id == 1 { Some(String::from("Alice")) }
    else { None }
}

let user = find_user(1);
match user {
    Some(name) => println!("Found: {}", name),
    None => println!("Not found"),
}

// convenient methods
let name = find_user(1).unwrap_or("Anonymous".into());
let upper = find_user(1).map(|n| n.to_uppercase());
let len = find_user(1).and_then(|n| Some(n.len()));

// Result<T, E>: Ok(value) or Err(error)
fn parse_num(s: &str) -> Result<i32, std::num::ParseIntError> {
    s.parse()
}

match parse_num("42") {
    Ok(n) => println!("Parsed: {}", n),
    Err(e) => println!("Error: {}", e),
}

Trait 与 Trait 约束

Trait 定义共享行为(类似其他语言的接口)。类型用 'impl Trait for Type' 实现 trait。Trait 可以有默认方法实现。Trait 约束(<T: Trait>)将泛型限制为实现特定 trait 的类型。'where' 子句提高了复杂约束的可读性。Trait 通过静态分发(泛型)和动态分发(trait 对象 &dyn Trait)实现多态。

rust
// define a trait (interface)
trait Summary {
    fn summarize(&self) -> String;

    // default method
    fn author(&self) -> String {
        String::from("Unknown")
    }
}

struct Article { title: String, content: String }

impl Summary for Article {
    fn summarize(&self) -> String {
        format!("{}: {}", self.title, self.content)
    }
    // author() uses default implementation
}

let a = Article {
    title: "Rust".into(),
    content: "Great".into(),
};
println!("{}", a.summarize());
println!("Author: {}", a.author()); // Unknown

// generic with trait bound
fn print_summary<T: Summary>(item: &T) {
    println!("{}", item.summarize());
}

// multiple bounds with +, or where clause
fn display<T: Summary + std::fmt::Display>(item: &T) {}
// or: fn display<T>(item: &T) where T: Summary + std::fmt::Display {}

派生宏与 Trait 对象

#[derive(...)] 自动实现常见 trait:Debug(调试打印)、Clone(深拷贝)、PartialEq/Eq(== 比较)、Hash(用于 HashMap 键)、Copy(栈拷贝而非移动)。Trait 对象(&dyn Trait 或 Box<dyn Trait>)通过 vtable 实现运行时多态,有少量性能损失。尽可能使用泛型进行静态分发(零成本),需要异构集合时使用 trait 对象。

rust
// derive common traits automatically
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Point {
    x: i32,
    y: i32,
}

let p1 = Point { x: 1, y: 2 };
let p2 = p1.clone();          // Clone
println!("{:?}", p1);          // Debug
println!("{}", p1 == p2);      // PartialEq -> true

// trait object: dynamic dispatch
trait Animal {
    fn sound(&self) -> String;
}

struct Dog;
struct Cat;

impl Animal for Dog { fn sound(&self) -> String { "Woof".into() } }
impl Animal for Cat { fn sound(&self) -> String { "Meow".into() } }

// Vec of trait objects (dynamic dispatch via vtable)
let animals: Vec<Box<dyn Animal>> = vec![
    Box::new(Dog),
    Box::new(Cat),
];
for a in &animals {
    println!("{}", a.sound());
}
08

错误处理

Result 与 ? 运算符

? 运算符是传播错误的惯用方式。对于 Ok(v),它解包为 v。对于 Err(e),它立即从函数返回 Err(e)。? 还通过 From trait 转换错误类型,所以返回 Box<dyn Error> 的函数可以对任何错误类型使用 ?。对于 Option,? 提前返回 None。这使得错误处理简洁而不牺牲安全性。

rust
use std::fs;
use std::io;
use std::num::ParseIntError;

// ? propagates errors: if Err, return early; if Ok, unwrap
fn read_config(path: &str) -> Result<i32, io::Error> {
    let content = fs::read_to_string(path)?; // ? on io::Result
    Ok(content.len() as i32)
}

// ? converts error types via From
fn parse_and_read(path: &str) -> Result<i32, Box<dyn std::error::Error>> {
    let content = fs::read_to_string(path)?;     // io::Error -> Box
    let n: i32 = content.trim().parse()?;         // ParseIntError -> Box
    Ok(n)
}

// ? on Option too
fn first_char(s: &str) -> Option<char> {
    s.lines().next()?.chars().next()
}

Panic 与 Result

对不可恢复的错误(bug、违反不变量)使用 panic!——它表示编程错误。对预期的、可恢复的失败(用户输入、文件 I/O、网络)使用 Result。unwrap()/expect() 在错误时 panic——在测试、原型或可以证明值有效时可以接受。在生产代码中,优先使用 ? 和 match 进行正确的错误处理。

rust
// panic: unrecoverable error, crashes the program
fn divide(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic!("Division by zero!"); // unwinds stack
    }
    a / b
}

// Result: recoverable error, caller decides
fn safe_divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err(String::from("division by zero"))
    } else {
        Ok(a / b)
    }
}

// unwrap/expect: panic on Err (use sparingly)
let n: i32 = "42".parse().unwrap();       // panics on error
let n2: i32 = "42".parse().expect("valid number"); // panic with msg

// when to panic vs Result:
// panic: bugs, invariant violations, impossible states
// Result: expected failures (file not found, parse error)

自定义错误类型

自定义错误类型为你提供类型安全、结构化的错误处理。实现 Display(人类可读)和 Error(用于源链)。为每个底层错误实现 From,以便 ? 自动转换。像 thiserror(派生宏)或 anyhow(动态错误盒)这样的库可以减少样板代码。库使用 thiserror,应用程序使用 anyhow。

rust
use std::fmt;
use std::error::Error;

#[derive(Debug)]
enum AppError {
    Io(std::io::Error),
    Parse(std::num::ParseIntError),
    NotFound(String),
}

// implement Display (required by Error)
impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AppError::Io(e) => write!(f, "IO error: {}", e),
            AppError::Parse(e) => write!(f, "Parse error: {}", e),
            AppError::NotFound(name) => write!(f, "Not found: {}", name),
        }
    }
}

// implement Error
impl Error for AppError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            AppError::Io(e) => Some(e),
            AppError::Parse(e) => Some(e),
            _ => None,
        }
    }
}

// From impls for ? to work automatically
impl From<std::io::Error> for AppError {
    fn from(e: std::io::Error) -> Self { AppError::Io(e) }
}

匹配与组合 Result

Result 组合器允许函数式风格的错误处理而无需 match。map 转换 Ok 值,map_err 转换错误,and_then 链接可能失败的操作(flatMap)。unwrap_or 在错误时提供默认值。is_ok/is_err 检查而不消费。这些方法使错误处理链可读并避免深度嵌套。

rust
// match on Result
let result: Result<i32, &str> = Ok(42);
match result {
    Ok(n) => println!("Got: {}", n),
    Err(e) => println!("Error: {}", e),
}

// combinators
let r1: Result<i32, &str> = Ok(5);
let doubled = r1.map(|n| n * 2);           // Ok(10)
let mapped_err = r1.map_err(|e| format!("{}", e));

// and_then: chain fallible operations
let r2 = r1.and_then(|n| if n > 0 { Ok(n * 2) } else { Err("negative") });

// unwrap_or family
let val = r1.unwrap_or(0);        // 5 or 0
let val2 = r1.unwrap_or_default(); // 5 or T::default()
let val3 = r1.unwrap_or_else(|_| 0); // lazy default

// check variants
println!("{}", r1.is_ok());  // true
println!("{}", r1.is_err()); // false

使用 From 转换错误

? 运算符使用 From 转换错误。Box<dyn Error> 为所有标准错误实现 From,使其成为方便的通用类型。anyhow crate 提供 anyhow::Result,它添加上下文(例如,.context("failed to read config")?)。对于库,用 thiserror 定义特定的错误枚举;对于应用程序,使用 anyhow 以简化。

rust
use std::fs;
use std::num::ParseIntError;

// Without From: manual conversion needed
fn manual(path: &str) -> Result<i32, String> {
    let content = fs::read_to_string(path)
        .map_err(|e| e.to_string())?;  // manual convert
    let n: i32 = content.trim().parse()
        .map_err(|e| e.to_string())?;  // manual convert
    Ok(n)
}

// With Box<dyn Error>: auto-converts via From
fn boxed(path: &str) -> Result<i32, Box<dyn std::error::Error>> {
    let content = fs::read_to_string(path)?;  // auto-converts
    let n: i32 = content.trim().parse()?;      // auto-converts
    Ok(n)
}

// anyhow::Result (from anyhow crate) is ergonomic for apps
// fn anyhow_fn() -> anyhow::Result<i32> {
//     let n: i32 = something()?; // any error auto-converted
//     Ok(n)
// }
09

模块与 Crate

模块系统

Rust 的模块系统组织代码。'mod' 声明模块(内联或通过文件)。'pub' 使条目公开(默认是私有的)。'use' 创建路径的快捷方式。'pub use' 重新导出条目(对 API 设计有用)。文件系统反映模块树:mod network 可以是 src/network.rs 或 src/network/mod.rs。Crate 根是 lib.rs(库)或 main.rs(二进制)。

rust
// mod.rs or mod declaration in lib.rs/main.rs
// File: src/lib.rs
mod network {
    pub mod connection {
        pub fn connect() -> bool { true }
        fn disconnect() {} // private
    }
}

// use: bring paths into scope
use network::connection::connect;

// calling with full path
fn main() {
    network::connection::connect();
    connect(); // after 'use'
}

// re-export with pub use
pub use network::connection::connect as open_connection;

// nested file: src/network/connection.rs
// mod network; in lib.rs loads src/network/mod.rs
// which can have: pub mod connection;

私有性与可见性

Rust 中的私有性是模块作用域的。条目默认是私有的——只能在定义模块及其后代中访问。'pub' 使它们公开。'pub(crate)' 限制在当前 crate(对库内部有用)。'pub(super)' 限制在父模块。结构体字段有单独的可见性——pub 结构体可以有私有字段,需要构造函数。

rust
mod my_module {
    // private by default
    fn internal_helper() {}

    // public: accessible from outside
    pub fn public_api() {
        internal_helper(); // can call private within module
    }

    // pub(crate): visible within this crate only
    pub(crate) fn crate_wide() {}

    // pub(super): visible to parent module
    pub(super) fn parent_visible() {}

    // struct fields are private even if struct is pub
    pub struct Config {
        pub name: String,    // public field
        secret: String,      // private field
    }

    // enum variants inherit the enum's visibility
    pub enum Status {
        Active,  // public because Status is public
        Inactive,
    }
}

Use 语句与别名

use 语句将条目引入作用域,减少路径冗长。分组导入(use std::io::{self, Read})比多行更干净。Trait 必须在作用域内才能使用其方法——这就是为什么有时需要 'use std::io::Read' 即使你不按名称引用 Read。通配符导入(*)不鼓励使用,除了 preludes。

rust
// basic use
use std::collections::HashMap;
use std::fs::read_to_string;

// grouped use
use std::io::{self, Read, Write, BufRead};
// equivalent to:
// use std::io;
// use std::io::Read;
// use std::io::Write;
// use std::io::BufRead;

// aliasing with 'as'
use std::collections::HashMap as Map;

// glob import (use sparingly)
use std::prelude::v1::*;

// bringing trait methods into scope
use std::io::Read; // now .read() method is available
let mut f = std::fs::File::open("x")?;
let mut buf = String::new();
f.read_to_string(&mut buf)?; // Read trait method

Cargo 与外部 Crate

Cargo 是 Rust 的包管理器和构建系统。依赖放在 Cargo.toml 的 [dependencies] 下。Features 启用可选功能(减少编译时间/二进制大小)。'cargo add' 自动编辑 Cargo.toml。Crate 发布到 crates.io。Edition(2021)控制语言特性。Cargo 处理编译、测试、文档和发布。

rust
# Cargo.toml
[package]
name = "my_app"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
rand = "0.8"

# in Rust code
use serde::{Serialize, Deserialize};
use rand::Rng;

#[derive(Serialize, Deserialize)]
struct User { name: String, age: u32 }

fn main() {
    let mut rng = rand::thread_rng();
    let n: i32 = rng.gen_range(1..=100);
    println!("{}", n);
}

# CLI commands:
# cargo new my_app      # create new project
# cargo build           # compile
# cargo run             # compile + run
# cargo test            # run tests
# cargo add serde       # add dependency
# cargo update          # update dependencies

测试

测试使用 #[test] 属性。assert_eq!/assert_ne! 比较值。#[should_panic] 验证发生 panic。测试可以返回 Result 进行基于错误的断言。#[cfg(test)] 确保测试模块仅在测试时编译。单元测试与代码放在一起;集成测试放在 tests/ 目录。用 'cargo test' 运行。使用 #[ignore] 跳过不稳定的测试。

rust
// Unit tests in same file (convention: tests module)
pub fn add(a: i32, b: i32) -> i32 { a + b }

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
        assert_ne!(add(2, 3), 6);
    }

    #[test]
    fn test_with_message() {
        let result = add(1, 1);
        assert_eq!(result, 2, "Expected 2, got {}", result);
    }

    #[test]
    #[should_panic(expected = "division by zero")]
    fn test_panic() {
        panic!("division by zero");
    }

    #[test]
    fn test_result() -> Result<(), String> {
        if add(2, 2) == 4 { Ok(()) }
        else { Err(String::from("math is broken")) }
    }
}

// Integration tests: tests/integration_test.rs
// Run: cargo test
10

并发与文件 I/O

线程

std::thread::spawn 创建 OS 线程。如果闭包捕获变量,必须是 'move',将所有权转移给线程(防止 use-after-free)。join() 阻塞直到线程完成,返回 Result。Rust 的所有权系统在编译时防止数据竞争——你不能在没有同步(Arc<Mutex<T>>)的情况下在线程间共享可变数据。

rust
use std::thread;
use std::time::Duration;

// spawn a thread
let handle = thread::spawn(|| {
    for i in 0..5 {
        println!("spawned thread: {}", i);
        thread::sleep(Duration::from_millis(10));
    }
});

// main thread continues
for i in 0..3 {
    println!("main thread: {}", i);
}

// wait for spawned thread to finish
handle.join().unwrap();

// move closure: transfer ownership to thread
let data = vec![1, 2, 3];
let h = thread::spawn(move || {
    println!("got data: {:?}", data); // data moved here
});
h.join().unwrap();

通道(消息传递)

通道通过消息传递实现线程通信(Go 的座右铭:'通过通信共享内存')。mpsc 允许多个发送者(tx.clone())但一个接收者。send() 返回 Result(如果接收者被丢弃则为 Err)。接收者实现 Iterator,所以 for 循环自然工作。对于多个接收者,使用 crossbeam-channel 或异步通道。消息传递避免了共享可变状态的复杂性。

rust
use std::sync::mpsc;
use std::thread;

// mpsc: multiple producer, single consumer
let (tx, rx) = mpsc::channel();

// clone transmitter for multiple producers
let tx2 = tx.clone();

thread::spawn(move || {
    let vals = vec!["a", "b", "c"];
    for v in vals {
        tx.send(v).unwrap();
    }
});

thread::spawn(move || {
    tx2.send("from tx2").unwrap();
});

// receiver is an iterator
for received in rx {
    println!("Got: {}", received);
}

Mutex 与 Arc(共享状态)

Arc(原子引用计数)实现跨线程的共享所有权(线程安全的 Rc)。Mutex 提供独占访问——lock() 阻塞直到获取,返回 MutexGuard,在 drop 时释放锁。RwLock 允许多个读取者或一个写入者。组合 Arc<Mutex<T>> 是共享可变状态的标准模式。lock() 上的 unwrap() 处理中毒(持有锁时线程 panic)。

rust
use std::sync::{Arc, Mutex};
use std::thread;

// Arc: thread-safe reference counting
// Mutex: mutual exclusion lock
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 || {
        // lock() returns MutexGuard, auto-unlocks when dropped
        let mut num = counter.lock().unwrap();
        *num += 1;
    }); // lock released here
    handles.push(handle);
}

for h in handles { h.join().unwrap(); }

println!("Result: {}", *counter.lock().unwrap()); // 10

// RwLock: multiple readers OR one writer
use std::sync::RwLock;
let data = RwLock::new(5);
let r1 = data.read().unwrap();  // shared read
let r2 = data.read().unwrap();
// let w = data.write().unwrap(); // would block until r1, r2 dropped

文件 I/O

fs::read_to_string 对小文件很方便。对于大文件,使用 BufReader/BufWriter 减少系统调用。Read/Write trait 提供低级字节操作。BufRead 添加 lines() 和 read_line() 用于文本。始终用 ? 处理错误(文件可能丢失、权限拒绝、磁盘满)。flush() 确保缓冲数据到达 OS(但不一定到磁盘)。

rust
use std::fs;
use std::io::{Read, Write, BufReader, BufWriter};
use std::fs::File;

// read entire file
let content = fs::read_to_string("file.txt")?;
fs::write("output.txt", "Hello")?;

// buffered read (efficient for large files)
let file = File::open("file.txt")?;
let mut reader = BufReader::new(file);
let mut buf = String::new();
reader.read_to_string(&mut buf)?;

// line by line
use std::io::BufRead;
let file = File::open("file.txt")?;
for line in BufReader::new(file).lines() {
    println!("{}", line?);
}

// buffered write
let file = File::create("out.txt")?;
let mut writer = BufWriter::new(file);
writeln!(writer, "Line 1")?;
writeln!(writer, "Line 2")?;
writer.flush()?; // ensure written to disk

Async/Await(Tokio)

Async/await 实现无需 OS 线程的高效并发——任务在线程池上运行并在 .await 点让出。tokio 是最流行的异步运行时。async fn 返回必须被 .await 的 Future。tokio::join! 并发运行 future 并等待所有完成。tokio::select! 竞争 future。异步非常适合 I/O 密集型工作(网络、文件);CPU 密集型工作使用线程。.await 不阻塞线程——它将控制权交还给运行时。

rust
use tokio::{fs, task, time};
use std::time::Duration;

// async fn returns a Future
async fn fetch_data() -> String {
    time::sleep(Duration::from_secs(1)).await;
    String::from("data")
}

// spawn concurrent tasks
#[tokio::main]
async fn main() {
    // run tasks concurrently
    let (a, b, c) = tokio::join!(
        fetch_data(),
        fetch_data(),
        fetch_data()
    );
    println!("{} {} {}", a, b, c);

    // spawn a background task
    let handle = tokio::spawn(async {
        let data = fs::read_to_string("file.txt").await.unwrap();
        println!("Read {} bytes", data.len());
    });
    handle.await.unwrap();

    // select: first to complete wins
    tokio::select! {
        val = fetch_data() => println!("Got: {}", val),
        _ = time::sleep(Duration::from_millis(500)) => {
            println!("Timeout!");
        }
    }
}
11

生命周期深入

函数中的命名生命周期

生命周期是编译器跟踪引用有效性的方式。'a 是一个泛型生命周期参数——它不改变运行时行为,只进行编译时检查。当函数接受多个引用并返回一个时,你必须注解生命周期,以便编译器知道返回的引用不会比其输入活得更久。这在编译时防止悬空指针。

rust
// Lifetimes tell the compiler how long references live
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
// 'a means: the returned reference lives as long as
// the SHORTEST of x and y

let s1 = String::from("long string");
let result;
{
    let s2 = String::from("hi");
    result = longest(s1.as_str(), s2.as_str());
    // result valid here
    println!("{}", result);
}
// result NOT valid here — s2 is dropped

生命周期省略规则

生命周期省略规则让你在常见情况下省略显式生命周期注解。规则 1 为每个引用参数分配一个不同的生命周期。规则 2 如果只有一个输入引用,则将该生命周期分配给输出。规则 3 适用于方法——输出获得 &self 的生命周期。当这些都无法解析所有引用时,你必须编写显式生命周期。大多数惯用 Rust 代码很少需要显式生命周期注解。

rust
// The compiler applies 3 elision rules automatically:
// 1. Each input reference gets its own lifetime
// 2. If one input lifetime, output gets that lifetime
// 3. If &self/&mut self, output gets self's lifetime

// These DON'T need explicit annotations (rule 2):
fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    for (i, &byte) in bytes.iter().enumerate() {
        if byte == b' ' { return &s[0..i]; }
    }
    &s[..]
}

// This NEEDS annotation (multiple inputs, rule doesn't apply):
// fn longest<'a>(x: &'a str, y: &'a str) -> &'a str

'static 生命周期

'static 是最长的生命周期——它持续整个程序运行时间。所有字符串字面量都有这个生命周期,因为它们嵌入在二进制文件中。当你看到 T: 'static 作为约束时,它不意味着 T 必须永远存在——它意味着 T 不能包含任何比 'static 短的引用(即,像 String、Vec、i64 这样的拥有类型总是满足此条件)。线程需要 'static,因为它们可能比调用函数活得更久。

rust
// 'static means the reference lives for the entire program
let s: &'static str = "I live forever";
// All string literals are &'static str

// Static variables (global, program-wide)
static COUNTER: AtomicUsize = AtomicUsize::new(0);
COUNTER.fetch_add(1, Ordering::SeqCst);

// 'static in bounds: T: 'static means T contains no
// non-static references (owned data always satisfies this)
fn spawn_thread<T: Send + 'static>(t: T) {
    std::thread::spawn(move || {
        drop(t);
    });
}

结构体中的生命周期

当结构体持有引用(不是拥有类型)时,它需要一个生命周期参数来声明该引用有效多久。结构体实例不能比它借用的数据活得更久。这对于零拷贝解析器、借用数据的迭代器和视图很常见。生命周期必须在结构体和其 impl 块上都声明。除非有特定原因需要借用,否则优先拥有数据(String、Vec)。

rust
// Structs holding references need lifetime annotations
struct Parser<'a> {
    text: &'a str,
    pos: usize,
}

impl<'a> Parser<'a> {
    fn new(text: &'a str) -> Self {
        Parser { text, pos: 0 }
    }
    fn peek(&self) -> Option<char> {
        self.text[self.pos..].chars().next()
    }
    fn advance(&mut self) {
        self.pos += 1;
    }
}

// The struct cannot outlive the text it borrows
let text = String::from("hello");
let mut p = Parser::new(&text);
p.advance();

多个生命周期与子类型

具有多个以不同方式交互的引用的函数需要多个生命周期参数。生命周期子类型意味着更长的生命周期可以替代更短的(协变)——&'static 可以在期望 &'a 的任何地方使用。这就是为什么 'static 是所有生命周期的子类型。当输出的生命周期只取决于某些输入时使用多个生命周期,给编译器更多灵活性,给调用者更少约束。

rust
// Multiple lifetime parameters
fn parse<'src, 'ctx>(src: &'src str, ctx: &'ctx Context) -> &'src str {
    // returns something tied to src, not ctx
    src
}

// 'static: 'a is always true (static outlives everything)
fn static_ref<'a>(_: &'a str) -> &'static str {
    "constant"  // 'static coerces to 'a
}

// Variance: &'long can be used where &'short is expected
// (covariance) — longer lifetime is a subtype
fn use_ref<'short>(r: &'short str) {}
let long: &'static str = "hi";
use_ref(long);  // OK: 'static coerces to any 'short
12

智能指针(Box、Rc、Arc、RefCell)

Box<T> — 堆分配

Box<T> 是 Rust 最简单的智能指针——它在堆上分配一个值,单一所有权。用于递归类型(其大小在编译时无法知道)、不想在栈上移动的大值,以及用于动态分发的 trait 对象(Box<dyn Trait>)。Box 解引用为 T,所以它的行为就像内部值。除了堆分配本身外,它几乎没有开销。

rust
// Box moves data to the heap (single owner)
let b = Box::new(5);
println!("{}", b);  // derefs automatically

// Recursive types NEED Box (size unknown at compile time)
enum List {
    Cons(i32, Box<List>),
    Nil,
}
let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));

// Trait objects (dynamic dispatch)
let shapes: Vec<Box<dyn Draw>> = vec![
    Box::new(Circle { radius: 1.0 }),
    Box::new(Square { side: 2.0 }),
];
for s in &shapes { s.draw(); }

// Box has zero runtime overhead when dereferencing

Rc<T> — 引用计数(单线程)

Rc<T>(引用计数)为单线程场景启用多所有权。Rc::clone 增加引用计数而不是复制数据——廉价的指针拷贝。当最后一个 Rc 被丢弃时,值被丢弃。用于共享图节点、父子树或多个部分需要拥有相同数据的任何结构。Rc 不是线程安全的——多线程使用 Arc。Rc 是不可变的——你不能直接通过它修改。

rust
use std::rc::Rc;

// Rc allows multiple owners via reference counting
let a = Rc::new(String::from("shared"));
let b = Rc::clone(&a);  // increments count, doesn't copy
let c = Rc::clone(&a);

println!("count = {}", Rc::strong_count(&a));  // 3
// Data dropped when count reaches 0

// Shared graph/tree structures
struct Node {
    children: Vec<Rc<Node>>,
    value: i32,
}
let leaf = Rc::new(Node { children: vec![], value: 1 });
let branch = Rc::new(Node {
    children: vec![Rc::clone(&leaf)],
    value: 2,
});

Arc<T> — 原子引用计数(线程安全)

Arc<T>(原子引用计数)是 Rc 的线程安全对应物。它使用原子操作进行引用计数,使其可以安全地跨线程共享。权衡是比 Rc 稍多的开销。当你需要跨多个线程共享所有权时使用 Arc。要修改共享数据,将 Arc 与 Mutex(用于独占访问)或 RwLock(用于读密集型访问)结合使用。Arc::clone 很廉价——它只是增加一个原子计数器。

rust
use std::sync::Arc;
use std::thread;

// Arc: thread-safe version of Rc (atomic ref counting)
let data = Arc::new(vec![1, 2, 3, 4, 5]);

let handles: Vec<_> = (0..3).map(|i| {
    let data = Arc::clone(&data);  // atomic increment
    thread::spawn(move || {
        println!("Thread {} sees: {:?}", i, *data);
    })
}).collect();

for h in handles { h.join().unwrap(); }

// Arc is slightly slower than Rc due to atomic operations
// Only use Arc when actually sharing across threads

RefCell<T> — 内部可变性

RefCell<T> 提供内部可变性——你可以通过共享引用修改,借用规则在运行时而非编译时强制执行。borrow() 返回不可变引用,borrow_mut() 返回可变引用。违反规则(例如,两个可变借用)会在运行时导致 panic。当编译器无法证明借用安全性时(例如,图结构、测试中的模拟对象)使用 RefCell。Rc<RefCell<T>> 是可变共享单线程数据的经典模式。

rust
use std::cell::RefCell;

// RefCell moves borrow checking to RUNTIME
let cell = RefCell::new(vec![1, 2, 3]);

// Multiple immutable borrows OR one mutable borrow
{
    let mut borrowed = cell.borrow_mut();
    borrowed.push(4);
}  // borrow released here

{
    let r1 = cell.borrow();     // OK
    let r2 = cell.borrow();     // OK — multiple immutable
    println!("{:?} {:?}", r1, r2);
}

// cell.borrow_mut() while r1 alive → PANIC at runtime
// Combine with Rc: Rc<RefCell<T>> for mutable shared graphs

Weak<T> — 打破引用循环

Weak<T> 是一个非拥有引用,不影响强引用计数。这对于打破引用循环很重要:如果父级拥有子级(Rc)而子级拥有父级(Rc),两者都不会被释放——内存泄漏。解决方案是使反向引用为 Weak。upgrade() 返回 Option<Rc<T>>——如果值已被丢弃则为 None。用于子→父链接、缓存和观察者模式,你不想保持数据存活。

rust
use std::rc::{Rc, Weak, RefCell};

// Weak references don't contribute to the strong count
// — prevents memory leaks in cycles
struct Node {
    parent: RefCell<Weak<Node>>,        // weak: child → parent
    children: RefCell<Vec<Rc<Node>>>,   // strong: parent → children
}

let leaf = Rc::new(Node {
    parent: RefCell::new(Weak::new()),
    children: RefCell::new(vec![]),
});
let branch = Rc::new(Node {
    parent: RefCell::new(Weak::new()),
    children: RefCell::new(vec![Rc::clone(&leaf)]),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);

// Upgrade returns Option — parent may already be dropped
if let Some(p) = leaf.parent.borrow().upgrade() {
    println!("parent exists");
}
13

Trait 对象与动态分发

dyn Trait — 动态分发

dyn Trait 启用动态分发——具体类型在编译时被擦除,方法调用在运行时通过 vtable 进行。这让你可以在单个集合中存储异构类型(Vec<Box<dyn Animal>>)。权衡:少量运行时成本(vtable 间接,无内联)且类型在编译时无法知道。当具体类型集合在编译时未知或需要将不同类型组合在一起时使用 trait 对象。

rust
trait Animal {
    fn name(&self) -> &str;
    fn sound(&self) -> String;
}

struct Dog { name: String }
struct Cat { name: String }

impl Animal for Dog {
    fn name(&self) -> &str { &self.name }
    fn sound(&self) -> String { "Woof".into() }
}
impl Animal for Cat {
    fn name(&self) -> &str { &self.name }
    fn sound(&self) -> String { "Meow".into() }
}

// dyn Trait = erased type, runtime dispatch via vtable
let animals: Vec<Box<dyn Animal>> = vec![
    Box::new(Dog { name: "Rex".into() }),
    Box::new(Cat { name: "Whiskers".into() }),
];
for a in &animals {
    println!("{} says {}", a.name(), a.sound());
}

对象安全规则

只有当编译器可以为其构建 vtable 时,trait 才是对象安全的。两条规则:(1)方法不能返回 Self(具体类型被擦除,所以无法知道),(2)方法不能有泛型类型参数(vtable 需要为每种可能类型一个条目)。以 Sized 为超 trait 的 trait 也不是对象安全的。如果需要对象安全,将返回 Self 的方法重构为返回 Box<dyn Trait> 或使用单独的工厂函数。

rust
// Object-safe traits CAN be used as dyn Trait:
trait Draw {
    fn draw(&self);  // OK: &self, no generics
}

// NOT object-safe:
trait Bad {
    fn create() -> Self;        // returns Self — needs known type
    fn process<T>(&self, x: T); // generic method — vtable can't cover all T
    const SIZE: usize;          // associated const (sometimes OK)
}

// Workaround for Self returns — use a factory or Box<Self>
trait GoodFactory {
    fn new_boxed() -> Box<dyn GoodFactory>;
}

// Sized bound makes trait non-object-safe
// trait Foo: Sized {}  // NOT object-safe

Trait 对象与泛型

泛型使用单态化——编译器为每种具体类型生成函数的单独副本,启用静态分发和完全优化(内联)。这有零运行时成本但增加二进制大小。Trait 对象(dyn)使用单个函数和 vtable 查找——更小的二进制但每次调用有少量运行时成本。当性能重要且类型集合小/已知时选择泛型;当需要异构集合或预先不知道所有类型时选择 dyn。

rust
// Generics: monomorphization, static dispatch, zero overhead
fn max_generic<T: PartialOrd>(a: T, b: T) -> T {
    if a > b { a } else { b }
}
// Each type instantiation creates a separate function:
// max_generic::<i32>, max_generic::<f64>, etc.

// Trait objects: dynamic dispatch, single function
fn max_dyn(a: &dyn PartialOrd, b: &dyn PartialOrd) -> bool {
    // can't return — don't know size at compile time
    a.partial_cmp(b) == Some(std::cmp::Ordering::Less)
}

// Rule of thumb:
// - Few types, performance-critical → generics (static dispatch)
// - Many/unknown types, flexibility needed → dyn (dynamic dispatch)
// - Heterogeneous collections → must use dyn

Any Trait 与向下转型

Any trait 让你存储任何类型的值并通过向下转型在运行时恢复具体类型。downcast_ref::<T>() 返回 Option<&T>,downcast::<T>() 返回 Result<Box<T>, Box<dyn Any>>。这是 Rust 在编译时确实不知道类型时的逃生舱(插件系统、动态配置)。但是,当可能类型集合已知时优先使用枚举——它们更安全、更快、更惯用。Any 依赖于 TypeId,它为所有 'static 类型实现。

rust
use std::any::Any;

// Any enables runtime type checking & downcasting
let x: Box<dyn Any> = Box::new(42i32);

// downcast_ref returns Option<&T>
if let Some(n) = x.downcast_ref::<i32>() {
    println!("It's an i32: {}", n);
}

// downcast returns Option<T> (for Box)
let boxed: Box<dyn Any> = Box::new("hello");
if let Ok(s) = boxed.downcast::<&str>() {
    println!("Recovered: {}", s);
}

// Useful for: plugin systems, error types, heterogeneous storage
// Avoid overusing — prefer enums when the type set is known

默认 Trait 方法与超 Trait

Trait 可以提供实现者可以覆盖或按原样使用的默认方法实现。超 trait(trait Named: Shape)要求实现类型也实现超 trait——这创建了一个层次结构,其中 Named 类型保证有 area() 和 describe()。默认方法减少样板代码并启用'扩展方法'模式,即向 trait 添加方法自动使所有现有实现者受益而不破坏它们。

rust
trait Shape {
    fn area(&self) -> f64;
    // Default method — can be overridden
    fn describe(&self) -> String {
        format!("Shape with area {:.2}", self.area())
    }
}

// Supertrait: trait that requires another trait
trait Named: Shape {
    fn name(&self) -> &str;
}

struct Circle { radius: f64 }
impl Shape for Circle {
    fn area(&self) -> f64 { std::f64::consts::PI * self.radius.powi(2) }
}
impl Named for Circle {
    fn name(&self) -> &str { "Circle" }
}

let c = Circle { radius: 2.0 };
println!("{}", c.describe());  // uses default method
14

宏(声明式与过程式)

声明式宏(macro_rules!)

macro_rules! 创建通过模式匹配在编译时扩展的声明式宏。$(...),* 语法是重复匹配器——它匹配零个或多个逗号分隔的表达式。$x:expr 表示'匹配任何表达式并将其绑定到 x'。宏在类型检查之前扩展,所以它们可以生成适用于任何类型的代码。使用宏来减少泛型无法处理的样板代码(例如,可变参数、语法扩展)。

rust
// macro_rules! defines pattern-matching macros
macro_rules! vec_of {
    ($($x:expr),*) => {{
        let mut v = Vec::new();
        $( v.push($x); )*
        v
    }};
}

let nums = vec_of!(1, 2, 3, 4);

// Recursive macro: build a HashMap
macro_rules! hashmap {
    ($($k:expr => $v:expr),*) => {{
        let mut m = std::collections::HashMap::new();
        $( m.insert($k, $v); )*
        m
    }};
}
let config = hashmap!("host" => "localhost", "port" => 8080);

宏片段类型

片段说明符决定宏参数匹配哪种语法。:ident 匹配标识符(名称),:expr 匹配表达式(值),:ty 匹配类型,:block 匹配花括号块,:stmt 匹配语句,:literal 匹配字面量。选择正确的说明符很重要——:expr 最常见但当你想创建函数/变量名时需要 :ident。宏系统是卫生的:宏引入的标识符不会与周围代码冲突。

rust
// Common fragment specifiers:
macro_rules! build_fn {
    // $name:ident — identifier (function/variable name)
    // $body:block — a block { ... }
    // $ty:ty — a type
    // $expr:expr — an expression
    // $stmt:stmt — a statement
    // $lit:literal — a literal (string, number)
    ($name:ident, $ret:ty, $body:block) => {
        fn $name() -> $ret $body
    };
}

build_fn!(get_answer, i32, { 42 });
println!("{}", get_answer());

// :pat — pattern, :path — module path
// :meta — attribute meta-item, :vis — visibility

内置标准宏

Rust 附带许多内置宏。println!/eprintln! 打印到 stdout/stderr。dbg! 打印表达式的值及文件/行信息——非常适合调试(它还返回值)。assert!/assert_eq!/assert_ne! 用于测试和不变量。todo!/unimplemented! 用 panic 标记不完整的代码。file!/line!/module! 提供编译时位置信息。env!/option_env! 在编译时读取环境变量——适用于嵌入版本信息。

rust
// Formatting & printing
println!("x = {}, y = {:?}", 1, "two");
eprintln!("Error: {}", "oops");     // stderr
format!("{}-{}", "a", "b");          // returns String

// Debug helpers
dbg!(2 + 2);                          // prints [src.rs:1] 2 + 2 = 4
assert!(1 + 1 == 2);                  // panic if false
assert_eq!(2 + 2, 4);                 // panic if not equal
assert_ne!(1, 2);                     // panic if equal

// Code generation
todo!("not implemented yet");         // unimplemented!()
unimplemented!();
panic!("fatal: {}", "reason");

// Environment & file info
println!("{}:{}", file!(), line!());  // src.rs:1
println!("{}", env!("CARGO_PKG_NAME"));

过程式宏概述

过程式宏(proc macros)是接受 TokenStream 作为输入并产生 TokenStream 作为输出的 Rust 函数——完整的代码到代码转换。与声明式宏不同,它们可以进行任意计算。三种类型:派生宏(通过 #[derive] 添加 trait 实现)、属性宏(注解条目)和函数式宏(自定义语法如 sqlx::query!)。它们必须放在 proc-macro = true 的单独 crate 中。syn crate 解析 Rust 语法,quote! 生成代码。流行示例:serde、tokio、thiserror。

rust
// Procedural macros are Rust functions that transform code
// Three kinds (must be in a separate crate with proc-macro=true):

// 1. Derive macros — #[derive(MyTrait)]
#[derive(Debug, Clone, MyTrait)]
struct Point { x: f64, y: f64 }

// 2. Attribute macros — #[my_attr]
#[my_attr]
fn function() {}

// 3. Function-like macros — my_macro!(...)
sqlx::query!("SELECT * FROM users");

// Cargo.toml for a proc-macro crate:
// [lib]
// proc-macro = true
//
// [dependencies]
// syn = "2.0"   # parse Rust code
// quote = "1.0" # generate code
// proc-macro2 = "1.0"

宏卫生与常见模式

Rust 宏是卫生的——宏内部创建的标识符存在于单独的'语法上下文'中,不会意外捕获或遮蔽调用作用域中的变量。这防止了宏内部变量名与调用者冲突的微妙错误。stringify! 在编译时将任何 token 流转换为字符串字面量(适用于错误消息)。cfg_debug! 显示了一个常见模式:使用 cfg! 系统根据构建配置条件编译代码。

rust
// Hygiene: macro-introduced identifiers don't leak
macro_rules! using_temp {
    ($e:expr) => {
        let temp = $e;  // this 'temp' is distinct from caller's
        println!("{}", temp);
    };
}
let temp = 10;
using_temp!(temp + 5);  // no conflict — hygienic

// Conditional compilation macro
macro_rules! cfg_debug {
    ($($e:tt)*) => {
        #[cfg(debug_assertions)]
        { $($e)* }
    };
}
cfg_debug! {
    println!("Debug mode on");
}

// stringify! converts tokens to a string literal
let s = stringify!(a + b * c);  // "a + b * c"
15

Unsafe Rust

裸指针

裸指针(*const T、*mut T)是 Rust 从借用检查器逃逸的逃生舱。与引用不同,它们可以为 null,可以别名(多个指针指向相同数据),并且不跟踪生命周期。创建它们是安全的,但解引用需要 unsafe,因为编译器无法保证有效性。将裸指针用于 FFI(与 C 接口)、实现低级数据结构(链表、向量)和性能关键代码,你手动确保安全。始终记录为什么 unsafe 是健全的。

rust
// Raw pointers: *const T (immutable) and *mut T (mutable)
let x = 42;
let r1: *const i32 = &x;       // coerce from reference
let r2: *mut i32 = x as *mut i32;  // cast

// Can be null, can alias, no borrow checking
let null: *const i32 = std::ptr::null();

// Creating raw pointers is safe, but DEREFERENCING is unsafe
unsafe {
    println!("r1 = {}", *r1);
}

// Convert between types (transmute-like)
let bytes: [u8; 4] = [0x78, 0x56, 0x34, 0x12];
let ptr = bytes.as_ptr() as *const u32;
unsafe { println!("0x{:x}", *ptr); }  // little-endian int

Unsafe 块与函数

unsafe 不会关闭借用检查器——它让你做五件特定的事:(1)解引用裸指针,(2)调用 unsafe 函数,(3)实现 unsafe trait,(4)访问/修改 static mut,(5)访问 union 字段。unsafe 块使 unsafe 操作显式和局部化。unsafe fn 声明调用该函数需要维护编译器无法检查的不变量。get_unchecked 跳过边界检查以提高性能——仅在你已验证索引时安全。最小化 unsafe 表面积并将其封装在安全 API 后面。

rust
// unsafe block: a localized unsafe region
let ptr: *const i32 = &42;
let val = unsafe { *ptr };

// unsafe fn: the ENTIRE function body is unsafe
unsafe fn dangerous(ptr: *const u8) -> u8 {
    *ptr
}
// Callers must use unsafe block:
let b = 5u8;
unsafe { dangerous(&b) };

// Splitting borrows safely (compiler is conservative)
let mut v = vec![1, 2, 3, 4];
let len = v.len();
unsafe {
    let first = v.get_unchecked(0);   // no bounds check
    let last = v.get_unchecked(len - 1);
    println!("{} {}", first, last);
}

FFI — 调用 C 函数

FFI(外部函数接口)让 Rust 调用 C 函数,反之亦然。extern "C" 块声明外部 C 函数——调用它们是 unsafe 的,因为编译器无法验证其行为。#[no_mangle] 防止 Rust 重命名函数,以便 C 可以按名称找到它。#[repr(C)] 保证结构体布局匹配 C 的内存布局(Rust 默认可能为了效率重新排序字段)。将 FFI 用于系统调用、遗留库和性能关键的绑定。bindgen crate 从 C 头文件自动生成 FFI 声明。

rust
// extern "C" declares foreign functions
extern "C" {
    fn abs(x: i32) -> i32;
}

fn main() {
    let x = -5;
    let positive = unsafe { abs(x) };
    println!("{}", positive);  // 5
}

// Exporting Rust functions to C
#[no_mangle]  // prevent name mangling
pub extern "C" fn add(a: i64, b: i64) -> i64 {
    a + b
}

// C-compatible struct
#[repr(C)]
struct Point { x: f64, y: f64 }

实现 Unsafe Trait

Unsafe trait(如 Send、Sync)要求实现者维护编译器无法验证的不变量。Send 意味着类型可以安全地在线程间移动;Sync 意味着 &T 可以在线程间共享。编译器为大多数类型自动派生这些,但裸指针默认不是 Send/Sync。当你手动实现它们时,你承担线程安全的责任。static mut 需要 unsafe 访问,因为多个线程可能在其上竞争——优先使用原子(AtomicU64)或 Mutex。始终用 SAFETY 注释记录安全理由。

rust
// Some traits are unsafe to implement — the compiler can't verify invariants
use std::marker::Send;

// Send/Sync are auto-implemented, but sometimes you must manually impl
struct RawPointer<T>(*mut T);

// SAFETY: We guarantee the pointer is only used on one thread
unsafe impl<T> Send for RawPointer<T> where T: Send {}

// Splitting a slice into disjoint mutable parts
let mut v = vec![1, 2, 3, 4, 5, 6];
let (left, right) = v.split_at_mut(3);
// left = [1,2,3], right = [4,5,6] — disjoint, but compiler
// couldn't prove this without unsafe internally

// Global mutable state
static mut COUNTER: u64 = 0;
unsafe { COUNTER += 1; }  // unsafe: data races possible

Union 与内联汇编

Union 允许不同类型共享相同的内存位置——读取最后写入的字段是未定义行为,因此是 unsafe 的。它们主要用于与 C 的 FFI。transmute 将一种类型的位模式重新解释转换为另一种相同大小的类型——如果大小不同或类型不兼容则极其危险。内联汇编(asm!)让你直接嵌入 CPU 指令,适用于内核开发和极端优化。所有这些都是锋利的工具:仅在不存在安全替代方案时使用,并封装在安全抽象后面。

rust
// Unions: multiple fields share the same memory (like C unions)
#[repr(C)]
union IntOrFloat {
    i: i32,
    f: f32,
}

let mut u = IntOrFloat { i: 42 };
unsafe { println!("as int: {}", u.i); }
u.f = 3.14;
unsafe { println!("as float: {}", u.f); }
// Reading the WRONG field is undefined behavior!

// Inline assembly (nightly / asm!)
#[cfg(feature = "asm")]
unsafe fn halt() {
    std::arch::asm!("hlt", options(nostack));
}

// Transmute: reinterpret bits as another type (same size)
let bits: u32 = 0x40490FDB;  // ~3.14159 in IEEE 754
let pi: f32 = unsafe { std::mem::transmute(bits) };
16

迭代器深入

Iterator Trait 与创建

Iterator trait 只需要一个返回 Option<Item> 的 next() 方法——None 表示耗尽。其他所有东西(map、filter、collect)都建立在它之上。iter() 借用元素(&T),into_iter() 消费集合(产生拥有的 T),iter_mut() 产生 &mut T。范围(1..5、1..=5)直接是迭代器。字符串按字符(Unicode 标量值)或字节迭代。迭代器是惰性的——在你消费它们之前什么也不运行。

rust
// The Iterator trait: one required method
trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    // ... many provided methods (map, filter, etc.)
}

// Creating iterators
let v = vec![1, 2, 3];
let iter = v.iter();        // borrows: &i32
let into_iter = v.into_iter(); // owns: i32 (consumes v)
let mut_iter = v.iter_mut();   // mutably borrows: &mut i32

// Range is an iterator
for i in 1..=5 { print!("{} ", i); }  // 1 2 3 4 5

// String iteration
for c in "héllo".chars() { print!("{} ", c); }  // h é l l o
for b in "hi".bytes() { print!("{} ", b); }      // 104 105

适配器方法(惰性)

适配器方法转换迭代器并返回新迭代器——它们是惰性的,所以链式 map().filter().map() 创建零个中间集合。map 对每个元素应用函数。filter 保留谓词返回 true 的元素。take(n) 在 n 个元素后停止(适用于无限迭代器)。skip(n) 丢弃前 n 个。flat_map 映射并展平嵌套迭代器。enumerate 将每个元素与其索引配对。在消费者(collect、sum、for 循环)驱动迭代器之前什么也不执行。

rust
let nums = vec![1, 2, 3, 4, 5, 6];

// map: transform each element
let doubled: Vec<_> = nums.iter().map(|x| x * 2).collect();

// filter: keep elements matching predicate
let evens: Vec<_> = nums.iter().filter(|&&x| x % 2 == 0).collect();

// take / skip: limit or skip elements
let first3: Vec<_> = nums.iter().take(3).collect();     // [1,2,3]
let after2: Vec<_> = nums.iter().skip(2).collect();     // [3,4,5,6]

// flat_map: map then flatten one level
let words: Vec<_> = ["a b", "c"].iter()
    .flat_map(|s| s.split(' '))
    .collect();  // ["a","b","c"]

// enumerate: add index
for (i, v) in nums.iter().enumerate() {
    println!("{}: {}", i, v);
}

消费者方法

消费者方法驱动惰性迭代器链实际执行。collect() 将结果收集到实现 FromIterator 的任何集合(Vec、HashMap、String 等)。sum/product/count/fold 将迭代器归约为单个值。find/any/all 短路——它们在知道答案后立即停止,所以在无限迭代器上很高效。min/max 返回 Option(空迭代器为 None)。惰性适配器 + 最终消费者的组合意味着迭代器链在优化后与手写循环一样高效。

rust
let nums = vec![1, 2, 3, 4, 5];

// collect: gather into a collection
let v: Vec<i32> = nums.iter().copied().collect();
let s: std::collections::HashSet<i32> = nums.iter().copied().collect();

// sum, product, count
let total: i32 = nums.iter().sum();        // 15
let prod: i32 = nums.iter().product();     // 120
let count = nums.iter().count();           // 5

// reduce / fold: accumulate into a single value
let max = nums.iter().copied().reduce(i32::max);  // Some(5)
let sum = nums.iter().fold(0, |acc, &x| acc + x); // 15

// find / any / all: short-circuiting
let first_even = nums.iter().find(|&&x| x % 2 == 0);  // Some(2)
let has_neg = nums.iter().any(|&x| *x < 0);  // false
let all_pos = nums.iter().all(|&x| *x > 0);  // true

// min / max
nums.iter().copied().min();  // Some(1)
nums.iter().copied().max();  // Some(5)

自定义迭代器

要创建自定义迭代器,用 next() 方法实现 Iterator trait。一旦你这样做,你就免费获得所有 70+ 适配器和消费者方法。迭代器应跟踪自己的状态(当前位置等)并在耗尽时返回 None。为你的集合类型实现 IntoIterator 启用 for 循环语法。对于双向或随机访问,还实现 DoubleEndedIterator 或 ExactSizeIterator。这就是 Vec、HashMap、Range 和所有标准集合提供迭代的方式。

rust
// Build a custom iterator for a counter
struct Counter {
    current: usize,
    max: usize,
}

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

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

// Now all iterator methods work!
let sum: usize = Counter::new(5).sum();  // 0+1+2+3+4 = 10
let doubled: Vec<_> = Counter::new(3).map(|x| x * 10).collect();

无限与链式迭代器

Rust 迭代器可以是无限的——(1..) 永远生成自然数,repeat(x) 无限重复。这些是安全的,因为适配器是惰性的:take(n) 限制消费。cycle() 无限重复有限迭代器。chain() 顺序连接迭代器。zip() 按位置配对元素(在较短者处停止)。peekable() 让你查看下一个元素而不消费它——适用于解析器。惰性设计意味着无限迭代器在消费前不花费任何成本,编译器将链优化为紧密循环。

rust
use std::iter;

// Infinite iterators (use with take!)
let naturals = (1..).take(10);  // 1..10
let zeros = iter::repeat(0).take(5);  // [0,0,0,0,0]
let alternating = iter::repeat_with(|| rand::random::<u8>());

// cycle: repeat a finite iterator infinitely
let pattern = [1, 2, 3].iter().cycle().take(7);
// [1,2,3,1,2,3,1]

// chain: concatenate two iterators
let combined = [1, 2].iter().chain([3, 4].iter());
// [1,2,3,4]

// zip: pair elements from two iterators
let pairs: Vec<_> = [1, 2, 3].iter().zip(['a', 'b', 'c']).collect();
// [(1,'a'), (2,'b'), (3,'c')]

// peekable: look ahead without consuming
let mut iter = [1, 2, 3].iter().peekable();
if let Some(&&first) = iter.peek() { println!("{}", first); }
17

错误处理深入

Result 与 Option 组合器

组合器让你链式可能失败的操作而无需嵌套 match 表达式。map 转换 Ok 值,map_err 转换错误。and_then 链接本身返回 Result 的操作(错误的 flatmap)。ok_or 将 Option→Result 转换。unwrap_or/unwrap_or_else/unwrap_or_default 提供回退值。这些优雅地组合:parse().map().and_then().map_err() 创建一个管道,其中每一步都可能失败,第一个失败短路。在生产代码中优先使用组合器而非 unwrap()。

rust
// Result combinators chain operations without match
fn parse_and_double(s: &str) -> Result<i32, std::num::ParseIntError> {
    s.parse::<i32>().map(|n| n * 2)
}

// and_then: chain fallible operations
fn validate(n: i32) -> Result<i32, String> {
    if n > 0 { Ok(n) } else { Err("must be positive".into()) }
}
let result = "5".parse::<i32>().and_then(validate);  // Ok(5)

// map_err: transform the error type
let r = "abc".parse::<i32>()
    .map_err(|e| format!("Parse failed: {}", e));

// ok_or / ok_or_else: convert Option to Result
let opt: Option<i32> = None;
let val = opt.ok_or("missing value");  // Err("missing value")

// unwrap_or / unwrap_or_else / unwrap_or_default
let x: i32 = "abc".parse().unwrap_or(0);

自定义错误类型

自定义错误类型让你表示特定领域的失败。关键模式:为每个底层错误类型实现 From,以便 ? 运算符自动转换。这意味着你可以使用 ? 与 std::io::Error、ParseIntError 等而无需显式 map_err。实现 Display 使错误用户友好;Debug 用于开发者。基于枚举的错误在 Rust 中是惯用的——它们是穷尽的(编译器警告缺失的情况)且零成本(无堆分配)。这是使用 thiserror 之前的基础。

rust
#[derive(Debug)]
enum AppError {
    Io(std::io::Error),
    Parse(std::num::ParseIntError),
    NotFound(String),
    Unauthorized,
}

// Implement From for automatic conversion with ?
impl From<std::io::Error> for AppError {
    fn from(e: std::io::Error) -> Self { AppError::Io(e) }
}
impl From<std::num::ParseIntError> for AppError {
    fn from(e: std::num::ParseIntError) -> Self { AppError::Parse(e) }
}

impl std::fmt::Display for AppError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            AppError::Io(e) => write!(f, "IO error: {}", e),
            AppError::Parse(e) => write!(f, "Parse error: {}", e),
            AppError::NotFound(s) => write!(f, "Not found: {}", s),
            AppError::Unauthorized => write!(f, "Unauthorized"),
        }
    }
}

// Now ? works for io::Error and ParseIntError automatically
fn read_config() -> Result<i32, AppError> {
    let s = std::fs::read_to_string("config.txt")?;  // io → AppError
    let n: i32 = s.trim().parse()?;                   // parse → AppError
    Ok(n)
}

Error Trait 与 Box<dyn Error>

std::error::Error 是错误类型的标准库 trait(需要 Debug + Display)。Box<dyn Error> 是最简单的错误类型——它通过 ? 接受任何错误,非常适合原型设计或不需要以编程方式处理特定错误的应用程序。缺点:你丢失了具体错误类型,所以匹配特定变体需要 downcast_ref。对于库,优先使用具体的枚举错误类型(用 thiserror)。对于应用程序,anyhow 是比 Box<dyn Error> 更好的选择,因为它保留回溯和错误链。

rust
use std::error::Error;

// std::error::Error is the trait for all errors
// Requires: Debug + Display
fn do_something() -> Result<(), Box<dyn Error>> {
    let f = std::fs::read_to_string("file.txt")?;  // io::Error
    let n: i32 = f.parse()?;                         // ParseIntError
    println!("{}", n);
    Ok(())
}

// Box<dyn Error> is the quick-and-dirty error type
// — accepts any error via ?, but loses specific type info

// downcast to recover specific error type
match do_something() {
    Err(e) => {
        if let Some(io_err) = e.downcast_ref::<std::io::Error>() {
            println!("IO: {}", io_err);
        }
    }
    _ => {}
}

thiserror Crate(库错误)

thiserror 是库错误类型的标准 crate。#[derive(Error)] 宏自动生成 Display(从 #[error("...")])和 From(从 #[from])实现。#[from] 使 ? 将底层错误转换为你的枚举变体。这消除了样板代码同时保持强类型、穷尽的错误枚举。为库使用 thiserror(调用者需要匹配特定错误)。{0} 占位符插入内部错误的 Display;命名字段如 {id} 插入结构体字段。

rust
use thiserror::Error;

// thiserror auto-generates Display and From impls
#[derive(Debug, Error)]
enum DataError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("parse failed: {0}")]
    Parse(#[from] std::num::ParseIntError),

    #[error("item {id} not found")]
    NotFound { id: u32 },

    #[error("invalid state: {msg}")]
    Invalid { msg: String },
}

// #[from] auto-implements From, so ? just works:
fn load() -> Result<i32, DataError> {
    let s = std::fs::read_to_string("data.txt")?;  // auto-converts
    let n: i32 = s.trim().parse()?;
    Ok(n)
}

anyhow Crate(应用程序错误)

anyhow 是应用程序/二进制文件错误处理的标准 crate。anyhow::Error 包装实现 std::error::Error 的任何错误并添加上下文、回溯和错误链。context() 为每个可能失败的步骤附加人类可读的消息,创建一个链如'读取配置失败:IO 错误:没有这样的文件'。这使调试更容易——你确切看到哪一步失败以及为什么。为 main() 和只需要报告错误而不需要匹配的应用程序代码使用 anyhow。为调用者需要类型化错误的库使用 thiserror。

rust
use anyhow::{Context, Result, anyhow};

// anyhow::Result = Result<T, anyhow::Error>
fn load_config() -> Result<String> {
    let content = std::fs::read_to_string("config.toml")
        .context("Failed to read config.toml")?;  // add context
    Ok(content)
}

fn main() -> Result<()> {
    let config = load_config()?;
    if config.is_empty() {
        // bail! / anyhow! create errors with format! syntax
        return Err(anyhow!("config is empty"));
    }
    println!("{}", config);
    Ok(())
}

// Error chain: "Failed to read config.toml: No such file..."
// anyhow preserves the full chain and backtrace
18

Cargo 与 Crate 深入

Cargo.toml 结构

Cargo.toml 是 Rust 项目的清单文件。[package] 描述 crate 元数据。[dependencies] 列出外部 crate——版本字符串使用 semver(^1.0 表示 >=1.0, <2.0)。features 启用可选功能(serde 的 derive 功能启用 #[derive(Serialize)])。[dev-dependencies] 仅用于测试/基准测试。[features] 定义条件编译标志。[profile.release] 控制优化设置。edition 字段(2015/2018/2021)决定语言特性——始终使用最新的。

rust
[package]
name = "myapp"
version = "0.1.0"
edition = "2021"
authors = ["You <[email protected]>"]
license = "MIT"
description = "A sample app"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", optional = true }
rand = "0.8"

[dev-dependencies]
criterion = "0.5"  # only for tests/benches

[features]
default = ["tokio"]
async-mode = ["dep:tokio"]

[[bin]]
name = "myapp"
path = "src/main.rs"

[profile.release]
opt-level = 3
lto = true
strip = true

依赖与 Feature 标志

Feature 标志启用条件编译。每个依赖可以暴露 features(例如,serde 的 derive)。default-features = false 去除默认功能以减小二进制大小。可选依赖(optional = true)仅在 feature 通过 dep:name 启用时编译。Features 是累加的——它们开启东西,从不关闭。这确保 feature 统一:如果两个依赖启用 serde 的不同功能,Cargo 用所有功能的并集编译一次 serde。使用 #[cfg(feature = "x")] 条件编译代码。

rust
# Cargo.toml
[dependencies]
# Version requirements: ^1.2 (compatible), =1.2.3 (exact), >=1.0,<2.0 (range)
serde = "1.0"                    # ^1.0 (default: compatible)
serde = { version = "1.0", features = ["derive"] }
serde = { version = "1.0", default-features = false }  # disable defaults

# Optional dependencies (enabled by a feature)
tokio = { version = "1", optional = true }

[features]
# "async" feature enables the optional tokio dep
async = ["dep:tokio"]
# Features can enable other features
full = ["async", "serde/derive"]

# In code:
# #[cfg(feature = "async")]
# fn run_async() { ... }

工作空间(多 Crate 项目)

工作空间将多个共享 Cargo.lock 和目标目录的相关 crate 分组。这加速构建(共享编译缓存)并确保所有 crate 使用相同的依赖版本。成员可以通过 path = "../core" 相互依赖。[workspace.dependencies] 集中版本管理——成员 crate 用 { workspace = true } 引用它们。resolver = "2"(edition 2021 中的默认值)使用按目标的 feature 统一,避免一些构建问题。为 monorepo、具有多个组件的库或拆分核心/CLI/服务器的项目使用工作空间。

rust
# Root Cargo.toml — workspace manifest
[workspace]
members = [
    "core",
    "cli",
    "server",
    "utils",
]
resolver = "2"

# Shared dependencies across all members
[workspace.dependencies]
serde = "1.0"
tokio = "1"

# In member crates (e.g., cli/Cargo.toml):
# [dependencies]
# serde = { workspace = true }
# core = { path = "../core" }  # local path dependency

构建配置与优化

Profile 控制 cargo 如何构建你的项目。dev(cargo build 的默认值)优先考虑编译速度。release(cargo build --release)优先考虑运行时性能。关键旋钮:opt-level(0-3,'s' 为大小,'z' 为最小大小)、lto(跨 crate 边界的链接时优化)、codegen-units(1 = 最佳优化但编译最慢)、strip(移除符号以获得更小的二进制)、panic = 'abort'(禁用展开,更小的二进制)。对于生产,使用 lto = true、codegen-units = 1、strip = true。自定义 profile 继承自现有 profile。

rust
# Cargo.toml
[profile.dev]
opt-level = 0        # no optimization (fast compile)
debug = true         # include debug symbols
overflow-checks = true

[profile.release]
opt-level = 3        # max optimization
lto = "fat"          # link-time optimization across crates
codegen-units = 1    # single codegen unit (better opt, slower compile)
strip = true         # strip debug symbols from binary
panic = "abort"      # smaller binary, no unwinding

[profile.release.package."*"]
opt-level = 2  # optimize dependencies less than your code

# Custom profile
[profile.bench]
inherits = "release"
debug = true  # keep symbols for profiling

# Usage: cargo build --release --profile bench

发布与文档

发布到 crates.io 是永久的——版本不能被覆盖或删除(只能 yank,阻止新的依赖者)。确保设置了 name、version、description、license 和 repository。cargo package 验证清单并显示将要发布的内容。cargo doc 从 /// 文档注释生成 HTML 文档——文档测试(``` 块中的代码)由 cargo test 编译和运行。带有示例的良好文档注释既是文档也是测试。使用 #[doc(hidden)] 隐藏内部条目。

rust
# Before publishing:
# 1. Login (one-time)
# $ cargo login <token>  (token from crates.io)

# 2. Check the package
# $ cargo package        # creates .crate file, checks metadata
# $ cargo publish --dry-run

# 3. Publish
# $ cargo publish        # uploads to crates.io (irreversible!)

# Required fields in Cargo.toml for publishing:
# name, version, description, license, repository

# Documentation:
# $ cargo doc            # generate docs for your crate + deps
# $ cargo doc --open     # generate and open in browser
# $ cargo doc --no-deps  # only your crate

# Doc tests run automatically:
/// Adds two numbers.
/// 
/// # Examples
/// ```
/// let result = mycrate::add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 { a + b }
19

Trait 对象与动态分发

dyn Trait 基础

Trait 对象(dyn Trait)启用运行时多态:单个变量可以持有实现相同 trait 的不同具体类型。编译器为每种类型生成 vtable(虚方法表),方法调用通过 vtable 进行(动态分发)。这有少量运行时成本但启用异构集合。当具体类型在编译时未知或变化时使用 trait 对象。

rust
trait Draw {
    fn draw(&self);
}

struct Circle { radius: f64 }
struct Square { side: f64 }

impl Draw for Circle {
    fn draw(&self) { println!("Circle r={}", self.radius); }
}
impl Draw for Square {
    fn draw(&self) { println!("Square s={}", self.side); }
}

// Trait object: type-erased, dynamic dispatch
let shapes: Vec<Box<dyn Draw>> = vec![
    Box::new(Circle { radius: 1.0 }),
    Box::new(Square { side: 2.0 }),
];
for s in &shapes { s.draw(); }

// Function taking trait object
fn render(shape: &dyn Draw) { shape.draw(); }

对象安全

只有当以下条件满足时,trait 才是对象安全的(可以用作 dyn Trait):没有返回 Self 的方法、没有按值接受 Self 的方法、没有泛型方法,且所有方法都是可分发的。Clone、Default 和 From 不是对象安全的。变通方法包括使用 Box<Self> 返回(box_clone 模式)、拆分 trait 或使用枚举静态分发。编译器清楚地报告对象安全违规。

rust
// Object-safe trait (can be made into dyn)
trait Animal {
    fn sound(&self) -> String;
    fn name(&self) -> &str;
}

// NOT object-safe: returns Self
trait Clone {
    fn clone(&self) -> Self;  // Self is unknown for dyn
}

// NOT object-safe: takes Self by value
trait Add {
    fn add(&self, other: Self) -> Self;
}

// NOT object-safe: generic method
trait From {
    fn from<T>(t: T) -> Self;
}

// Fix: use where clauses or separate traits
trait AnimalSafe {
    fn sound(&self) -> String;
    fn box_clone(&self) -> Box<dyn AnimalSafe>;
}

静态与动态分发

静态分发(带 trait 约束的泛型)单态化:编译器为每种具体类型生成专用版本,启用内联和最大性能,代价是二进制大小。动态分发(dyn Trait)在运行时使用 vtable 查找,更小的二进制但更慢的调用(阻止内联)。性能关键代码优先使用静态分发;异构集合和插件系统使用动态分发。

rust
// Static dispatch (monomorphization)
fn max<T: Ord>(a: T, b: T) -> T {
    if a > b { a } else { b }
}
// Compiler generates max_i32, max_f64, etc.

// Dynamic dispatch (vtable lookup)
fn max_dyn(a: &dyn Ord, b: &dyn Ord) -> bool {
    // a > b  // Cannot use operators on dyn
    false
}

// Trait bound (static)
fn process<T: Display>(item: &T) {
    println!("{}", item);
}

// impl Trait (static, syntactic sugar)
fn process2(item: &impl Display) {
    println!("{}", item);
}

// dyn Trait (dynamic)
fn process3(item: &dyn Display) {
    println!("{}", item);
}

带生命周期的 Trait 对象

Trait 对象可以携带生命周期约束:Box<dyn Trait + 'a> 意味着 trait 对象(及其背后的具体类型)必须至少存活 'a。默认情况下,Box<dyn Trait> 暗示 'static。当存储可能持有引用的 trait 对象时,显式添加生命周期。+ 语法将 trait 约束与生命周期结合。这在插件系统和事件处理程序中很常见。

rust
trait Parser {
    fn parse(&self, input: &str) -> &str;
}

// Trait object with lifetime
fn make_parser() -> Box<dyn Parser> {
    Box::new(MyParser)
}

// Trait object holding references
struct Runner<'a> {
    parsers: Vec<Box<dyn Parser + 'a>>,
}

impl<'a> Runner<'a> {
    fn add(&mut self, p: Box<dyn Parser + 'a>) {
        self.parsers.push(p);
    }
}

// dyn Trait defaults to 'static when no lifetime given
fn static_parser() -> Box<dyn Parser> {
    Box::new(MyParser)
}

向下转型与 Any

Any trait 启用运行时类型检查和向下转型。Any 自动为所有 'static 类型实现。downcast_ref 和 downcast_mut 返回 Option,允许安全的类型恢复。这对于插件系统、动态配置和异构容器很有用。谨慎使用 Any——它绕过类型系统。对于已知的替代方案优先使用枚举,对于类型安全的多态优先使用泛型。

rust
use std::any::Any;

// Any enables runtime type identification
let x: Box<dyn Any> = Box::new(42_i32);

// Downcast to concrete type
if let Some(n) = x.downcast_ref::<i32>() {
    println!("Got i32: {}", n);
}

// Store heterogeneous values
let mut bag: Vec<Box<dyn Any>> = vec![
    Box::new(42_i32),
    Box::new("hello".to_string()),
    Box::new(3.14_f64),
];

for item in &bag {
    if let Some(s) = item.downcast_ref::<String>() {
        println!("String: {}", s);
    } else if let Some(n) = item.downcast_ref::<i32>() {
        println!("i32: {}", n);
    }
}
20

声明式宏

macro_rules! 基础

macro_rules! 定义匹配模式并扩展为代码的声明式宏。$( $x:expr ),* 匹配逗号分隔的表达式列表,重复零次或多次。$() ... * 块为每个匹配重复。宏在编译时类型检查之前扩展。它们是卫生的:宏引入的标识符不会与周围代码冲突。使用宏减少样板代码(vec!、println!、format!)。

rust
macro_rules! vec_of {
    ( $( $x:expr ),* ) => {
        {
            let mut v = Vec::new();
            $(
                v.push($x);
            )*
            v
        }
    };
}

let nums = vec_of!(1, 2, 3, 4);
let strs = vec_of!("a", "b", "c");

// Multiple patterns
macro_rules! greet {
    () => { println!("Hello!") };
    ($name:expr) => { println!("Hello, {}!", $name) };
    ($name:expr, $greeting:expr) => {
        println!("{}, {}!", $greeting, $name)
    };
}
greet!();
greet!("Alice");
greet!("Bob", "Hi");

片段类型

宏片段有特定类型:expr(表达式)、stmt(语句)、ty(类型)、pat(模式)、ident(标识符)、tt(token 树,最灵活)、literal(字面量)等。片段类型决定宏接受什么以及如何解析。tt 是最通用的——任何有效的 token 序列。尽可能使用最具体的类型以获得更好的错误消息。解析器遵循最近添加歧义规则。

rust
macro_rules! items {
    // $x:expr - expression (1 + 2, foo())
    // $x:stmt - statement (let x = 5;)
    // $x:ty - type (Vec<i32>, &str)
    // $x:pat - pattern (Some(x), (a, b))
    // $x:path - path (std::vec::Vec, Module::Type)
    // $x:ident - identifier (foo, Bar)
    // $x:literal - literal (42, "hello")
    // $x:tt - token tree (anything)
    // $x:block - block ({ ... })
    // $x:item - item (fn, struct)

    ($e:expr, $t:ty, $i:ident) => {
        let $i: $t = $e;
    };
}

items!(42, i32, my_num);
items!("hi", &str, greeting);

重复模式

宏中的重复:$(...)* 匹配零个或多个,$(...)+ 匹配一个或多个,$(...)? 匹配零个或一个。分隔符如逗号在匹配之间。嵌套重复处理多维数据(矩阵、列表的列表)。$() ... * 扩展块为每个匹配重复。同一重复中的多个变量必须匹配相同的次数。使用 @prefix 内部规则进行累加器模式。

rust
macro_rules! sum {
    // Zero or more: $(...)*
    ( $( $x:expr ),* ) => {
        {
            let mut total = 0;
            $(
                total += $x;
            )*
            total
        }
    };

    // One or more: $(...)+
    ( first $(, $x:expr )+ ) => {
        println!("First and more");
    };

    // With separator and count
    ( $( $x:expr ),+ $(; $sep:expr )? ) => {
        println!("List with optional separator");
    };
}

// Nested repetition
macro_rules! matrix {
    ( $( [ $( $x:expr ),* ] ),* ) => {
        vec![ vec![ $( $x ),* ],* ]
    };
}

卫生与导出

宏卫生防止标识符冲突:宏引入的变量存在于自己的作用域中,不会捕获或遮蔽调用者变量。使用 $crate 引用定义宏的 crate 中的条目,确保它在重新导出后工作。@prefix 约定标记用户不应直接调用的内部辅助规则。#[macro_export] 在 crate 根发布宏。

rust
// Export at crate root
#[macro_export]
macro_rules! log {
    ($($arg:tt)*) => {
        // Hygienic: this T does not collide with caller's T
        let _ts: std::time::Instant = std::time::Instant::now();
        eprintln!("[{}] {}", "LOG", format!($($arg)*));
    };
}

// Use $crate to refer to crate items (works after re-export)
#[macro_export]
macro_rules! make_error {
    ($msg:expr) => {
        $crate::Error::new($msg)
    };
}

// Internal helper rule (convention: @ prefix)
macro_rules! count {
    (@count $x:expr, $($rest:expr),*) => { 1 + count!(@count $($rest),*) };
    (@count $x:expr) => { 1 };
    () => { 0 };
}

常见宏模式

宏擅长构建 DSL 和减少样板代码。常见模式:构建器 DSL(html!、sql!)、测试断言(assert_approx!)、配置(config!)和代码生成(类派生宏)。宏是卫生的和编译时的,所以它们没有运行时成本。限制:递归深度不超过 64、复杂的错误消息以及非平凡解析的困难。对于复杂的元编程,使用过程式宏。

rust
// 1. Builder DSL
macro_rules! html {
    ($tag:ident { $($body:tt)* }) => {
        format!("<{}>{}</{}>", stringify!($tag), html_inner!($($body)*), stringify!($tag))
    };
}

// 2. Test assertion
macro_rules! assert_approx {
    ($a:expr, $b:expr, $eps:expr) => {
        assert!(($a - $b).abs() < $eps, "{} != {} within {}", $a, $b, $eps);
    };
}

// 3. Configuration
macro_rules! config {
    ( $( $key:ident : $val:expr ),* ) => {
        {
            let mut m = std::collections::HashMap::new();
            $(
                m.insert(stringify!($key).to_string(), $val.to_string());
            )*
            m
        }
    };
}

let cfg = config! { host: "localhost", port: 8080 };
21

Cargo 与工作空间

工作空间设置

工作空间将共享依赖和目标目录的多个 crate 分组。成员显式列出或通过 glob。[workspace.package] 定义共享包元数据,用 .workspace = true 继承。[workspace.dependencies] 集中依赖版本,确保所有 crate 使用相同版本。这防止版本冲突并加速构建(单个 Cargo.lock)。为 CLI + 库 + 服务器等多 crate 项目使用工作空间。

rust
# Root Cargo.toml
[workspace]
members = ["crates/*", "cli", "server"]
resolver = "2"

[workspace.package]
version = "0.1.0"
edition = "2021"
authors = ["Team <[email protected]>"]
license = "MIT"

[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1.0"

# Member crate: crates/mylib/Cargo.toml
[package]
name = "mylib"
version.workspace = true
edition.workspace = true

[dependencies]
serde.workspace = true
tokio.workspace = true

构建配置

Profile 控制编译设置。dev 优先考虑快速编译(opt-level 0、调试符号)。release 最大化运行时性能(opt-level 3、LTO、单个 codegen 单元)。LTO(链接时优化)启用跨 crate 内联。panic = "abort" 产生更小的二进制但禁用展开。按包覆盖(profile.dev.package."*")甚至在 dev 中优化依赖。自定义 profile 继承自现有 profile。

rust
# Cargo.toml
[profile.dev]
opt-level = 0        # No optimization (fast compile)
debug = true         # Include debug symbols
overflow-checks = true

[profile.release]
opt-level = 3        # Max optimization
debug = false
lto = "fat"          # Link-time optimization
codegen-units = 1    # Single unit (better opt, slower compile)
panic = "abort"      # Smaller binary, no unwinding
strip = true         # Strip symbols

[profile.dev.package."*"]
opt-level = 2        # Optimize dependencies in dev

# Custom profile
[profile.bench]
inherits = "release"
debug = true

# Usage: cargo build --release, cargo build --profile=bench

Feature 与条件编译

Feature 启用条件编译。可选依赖自动成为 feature。cfg(feature = "...") 按 feature 门控代码。除非传递 --no-default-features,否则启用默认 feature 集。Feature 应该是累加的(启用更多,而不是更少)。使用 feature 减小二进制大小、支持多个后端或门控实验性代码。与 cfg_attr 结合用于条件派生宏。避免互斥的 feature。

rust
# Cargo.toml
[features]
default = ["json"]
json = ["serde_json"]
yaml = ["serde_yaml"]
async-runtime = ["tokio"]

[dependencies]
serde = { version = "1.0", optional = true }
serde_json = { version = "1.0", optional = true }
serde_yaml = { version = "0.9", optional = true }
tokio = { version = "1", optional = true, features = ["full"] }

# Code: conditional compilation
#[cfg(feature = "json")]
pub fn parse_json(s: &str) -> Result<Value, Error> {
    serde_json::from_str(s)
}

#[cfg(not(feature = "json"))]
pub fn parse_json(_: &str) -> Result<Value, Error> {
    Err(Error::FeatureNotEnabled)
}

构建脚本(build.rs)

build.rs 在编译之前运行,启用代码生成、环境嵌入和 C 库链接。cargo:rerun-if-* 指令控制脚本何时重新运行。用 println! 宏生成代码,然后 include! 到你的 crate 中。常见用途:嵌入版本信息、生成绑定(bindgen)、编译 protobuf/SQL 模式和链接系统库。保持构建脚本快速——它们在每次构建时运行。

rust
// build.rs: runs before compilation
use std::env;
use std::fs;
use std::path::Path;

fn main() {
    // Tell Cargo to rerun if env changes
    println!("cargo:rerun-if-env-changed=DATABASE_URL");

    let out_dir = env::var("OUT_DIR").unwrap();
    let dest = Path::new(&out_dir).join("config.rs");

    let db_url = env::var("DATABASE_URL")
        .unwrap_or_else(|_| "sqlite://default.db".to_string());

    // Generate Rust code at build time
    fs::write(&dest, format!(
        "pub const DATABASE_URL: &str = \"{}\";",
        db_url
    )).unwrap();

    // Link a C library
    println!("cargo:rustc-link-lib=static=mylib");
    println!("cargo:rustc-link-search=native=/usr/local/lib");
}

// In code: include generated file
include!(concat!(env!("OUT_DIR"), "/config.rs"));

发布与文档

cargo publish 将 crate 上传到 crates.io。使用 --dry-run 在发布前验证。cargo doc 从文档注释(///)生成 HTML 文档。文档注释中的代码块用 cargo test --doc 测试。包含 Examples、Panics 和 Errors 部分。元数据(description、repository、keywords)提高可发现性。一旦发布,版本不能被重用或删除——使用 yank 防止新项目依赖它。

rust
# Publish to crates.io
cargo login <token>     # One-time authentication
cargo publish           # Publish current crate

# Before publishing:
cargo publish --dry-run # Verify package contents
cargo package           # Inspect the .crate file

# Documentation
cargo doc               # Generate docs
cargo doc --open        # Generate and open
cargo doc --no-deps     # Only this crate

# In code: doc comments
/// Adds two numbers.
///
/// # Examples
/// ```
/// let result = mycrate::add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 { a + b }

# README and metadata in Cargo.toml
[package]
description = "A short description"
repository = "https://github.com/user/repo"
readme = "README.md"
keywords = ["parser", "cli"]
categories = ["command-line-utilities"]
22

过程式宏

宏类型

过程式宏在编译时生成 Rust 代码,操作 token 流。三种类型:函数式(custom!())、派生(#[derive(Custom)])和属性(#[custom])。它们需要 proc-macro = true 的单独 crate。syn crate 解析 Rust 语法,quote 生成代码,proc-macro2 启用测试。Proc-macro 强大但复杂——将它们用于派生宏、DSL 和声明式宏无法处理的代码生成。

rust
// Three types of procedural macros:
// 1. Function-like: my_macro!(...)
// 2. Derive: #[derive(MyMacro)]
// 3. Attribute: #[my_macro]

// Cargo.toml for a proc-macro crate
// [lib]
// proc-macro = true

// [dependencies]
// syn = { version = "2", features = ["full"] }
// quote = "1"
// proc-macro2 = "1"

use proc_macro::TokenStream;

#[proc_macro]
pub fn make_answer(_item: TokenStream) -> TokenStream {
    "fn answer() -> i32 { 42 }".parse().unwrap()
}

// Usage: make_answer!();
// Generates: fn answer() -> i32 { 42 }

派生宏

派生宏为用 #[derive(MyMacro)] 注解的类型添加 trait 实现。syn 将输入解析为 DeriveInput AST。quote! 用 # 插值变量生成代码。辅助属性(attributes(hello))允许在字段或变体上自定义。常见派生宏:Debug、Clone、Serialize、Deserialize。生成的代码被附加到模块,所以它不能修改原始类型。

rust
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    let name = &ast.ident;

    let expanded = quote! {
        impl HelloMacro for #name {
            fn hello() {
                println!("Hello from {}!", stringify!(#name));
            }
        }
    };

    expanded.into()
}

// Usage:
// #[derive(HelloMacro)]
// struct Pancakes;
// Pancakes::hello();  // "Hello from Pancakes!"

// Helper attributes
#[proc_macro_derive(HelloMacro, attributes(hello))]
pub fn hello_with_attr(input: TokenStream) -> TokenStream { /* ... */ }

属性宏

属性宏(#[my_attr])转换它们注解的条目,可能完全替换它。它们接收属性参数和被注解的条目。常见用途:日志记录、缓存、异步包装器(#[tokio::main])和路由(#[get("/path")])。属性宏可以更改条目的签名、添加代码或生成额外条目。它们比派生宏更灵活但更难正确使用。

rust
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn};

#[proc_macro_attribute]
pub fn log_calls(attr: TokenStream, item: TokenStream) -> TokenStream {
    let attr_args = syn::parse_macro_input!(attr as syn::AttributeArgs);
    let input_fn = parse_macro_input!(item as ItemFn);

    let fn_name = &input_fn.sig.ident;
    let fn_block = &input_fn.block;

    let expanded = quote! {
        fn #fn_name() {
            println!("Calling {}", stringify!(#fn_name));
            let __result = (|| #fn_block)();
            println!("Finished {}", stringify!(#fn_name));
            __result
        }
    };

    expanded.into()
}

// Usage:
// #[log_calls]
// fn my_function() -> i32 { 42 }

函数式宏

函数式过程宏(my_macro!())接受任意 token 流,启用自定义 DSL。实现 Parse 定义接受的语法。宏可以基于输入验证、转换或生成代码。常见用途:SQL 查询(sqlx)、HTML 模板(maud)和配置 DSL。与声明式宏不同,proc-macro 可以解析复杂语法并在编译时执行任意计算。保持它们快速以避免缓慢的构建。

rust
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse::Parse, parse::ParseStream, parse_macro_input};

// Custom syntax: sql!(SELECT * FROM users WHERE id = $1)
struct SqlQuery {
    query: String,
}

impl Parse for SqlQuery {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let query = input.to_string();
        Ok(SqlQuery { query })
    }
}

#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
    let SqlQuery { query } = parse_macro_input!(input as SqlQuery);

    let expanded = quote! {
        {
            static QUERY: &str = #query;
            // Compile-time SQL validation could go here
            QUERY
        }
    };

    expanded.into()
}

测试与调试

测试 proc-macro:trybuild 运行 UI 测试,比较编译器输出(成功或错误消息)与预期文件。对于派生宏,测试生成的代码是否编译和行为正确。用 eprintln!(编译期间打印)或 cargo expand(显示展开的宏输出)调试。宏开发是迭代的:编写宏、用 cargo expand 检查输出、修复问题。清楚地记录宏的语法和支持的功能。

rust
// Cargo.toml
// [dev-dependencies]
// trybuild = "1"

// tests/ui/my_macro.rs - test file
// #[derive(MyMacro)]
// struct Foo;
// fn main() { Foo::hello(); }

// tests/ui/my_macro.stderr - expected error
// error: ...

// Test runner
#[test]
fn ui() {
    let t = trybuild::TestCases::new();
    t.pass("tests/ui/pass_*.rs");
    t.compile_fail("tests/ui/fail_*.rs");
}

// Debugging with eprintln
#[proc_macro_derive(Debug)]
pub fn debug_derive(input: TokenStream) -> TokenStream {
    eprintln!("Input tokens: {}", input);
    let ast = syn::parse2(input.clone().into()).unwrap();
    eprintln!("Parsed AST: {:#?}", ast);
    TokenStream::new()
}
23

macro_rules!

macro_rules! 定义声明式宏。$x 是捕获,expr 匹配表达式。$(...)* 重复。宏在编译时展开。适用于减少样板代码。标准 vec! 宏类似工作。

rust
macro_rules! vec_of {
    ($($x:expr),*) => {{
        let mut v = Vec::new();
        $(v.push($x);)*
        v
    }};
}
let nums = vec_of!(1, 2, 3);

过程式宏

过程式宏在编译时生成代码。三种类型:派生(#[derive(Debug)])、属性(#[my_attr])、函数式(my_macro!)。比 macro_rules! 更强大但需要单独的 crate。被 serde、tokio 和 diesel 使用。

rust
// In a separate crate with proc-macro = true
use proc_macro::TokenStream;
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    // Generate impl HelloMacro for the type
    // Returns new TokenStream
}

常见宏

内置宏:println!/format! 用于输出,vec! 用于向量,assert!/assert_eq! 用于测试,dbg! 用于调试,todo!/unreachable! 用于控制流。全部基于 macro_rules!。dbg! 返回值用于链式调用。

rust
println!("Hello, {}!", "world");
format!("x = {}", 42);
vec![1, 2, 3];
assert!(1 + 1 == 2);
assert_eq!(2 + 2, 4);
dbg!(some_variable);  // Debug print
todo!("Not implemented");
unreachable!();

宏卫生

Rust 宏是卫生的:宏引入的标识符不会与调用作用域中的标识符冲突。这防止了微妙的错误。宏内部的 temp 与外部 temp 不同。声明式宏始终是卫生的。

rust
macro_rules! swap {
    ($a:expr, $b:expr) => {
        let temp = $a;
        $a = $b;
        $b = temp;
    };
}
// temp is hygienic: does not conflict with outer temp
let mut temp = 1;
let mut x = 2;
swap!(temp, x);  // Works correctly

重复

宏中的重复:$(...)* 匹配零个或多个,$(...)+ 一个或多个。可以指定分隔符(逗号)。$x 捕获每个值。适用于类可变参数函数。标准 println! 使用此功能处理多个参数。

rust
macro_rules! sum {
    ($($x:expr),*) => {
        0 $(+ $x)*
    };
}
let total = sum!(1, 2, 3, 4);  // 10
// $(...)* zero or more, $(...)+ one or more
// $(...),? optional trailing comma
24

Async 深入

async/await

async fn 返回 Future。.await 挂起直到 future 准备好。Future 是惰性的:在被 await 之前什么也不运行。编译器将 async fn 转换为状态机。使用 tokio 或 async-std 运行时执行。

rust
async fn fetch_data() -> String {
    // Simulate async work
    String::from("data")
}
async fn process() {
    let data = fetch_data().await;
    println!("{}", data);
}

Tokio 运行时

tokio::main 启用异步 main。spawn 创建任务(类似绿色线程)。join! 等待多个 future 并发完成。Tokio 提供 I/O、定时器和调度。Rust 中最流行的异步运行时。

rust
#[tokio::main]
async fn main() {
    let task1 = tokio::spawn(async { work1().await });
    let task2 = tokio::spawn(async { work2().await });
    let (r1, r2) = tokio::join!(task1, task2);
}

通道(异步)

mpsc(多生产者、单消费者)通道启用异步通信。send/recv 是异步的。通道有缓冲区(32 条消息)。当所有发送者 drop 时,recv 返回 None。适用于生产者-消费者模式。

rust
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
    let (tx, mut rx) = mpsc::channel(32);
    tokio::spawn(async move {
        tx.send("hello").await.unwrap();
    });
    while let Some(msg) = rx.recv().await {
        println!("{}", msg);
    }
}

Select

select! 等待多个 future 中第一个完成。其他 future 被丢弃。适用于超时和竞争操作。该模式在网络服务器中常见。每个分支可以有守卫模式。

rust
tokio::select! {
    result = task1 => {
        println!("Task1 done: {:?}", result);
    }
    result = task2 => {
        println!("Task2 done: {:?}", result);
    }
    _ = tokio::time::sleep(Duration::from_secs(5)) => {
        println!("Timeout");
    }
}

Stream

Stream 是 Iterator 的异步等价物。next().await 获取下一个条目。StreamExt 提供 map、filter、for_each。适用于处理来自网络或文件的数据块。async-stream crate 简化创建流。

rust
use tokio_stream::{self as stream, StreamExt};
let mut stream = stream::iter(vec![1, 2, 3]);
while let Some(item) = stream.next().await {
    println!("{}", item);
}
// Map, filter like iterators but async
stream.map(|x| x * 2).filter(|x| *x > 2).for_each(|x| async move {
    println!("{}", x);
}).await;
25

Cargo 与 Crate

Cargo.toml

Cargo.toml 是清单文件。[package] 定义元数据。[dependencies] 列出外部 crate。Features 启用可选功能。[dev-dependencies] 仅用于测试。Edition 2021 是最新的稳定版本。版本使用 semver。

rust
[package]
name = "myapp"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }

[dev-dependencies]
pretty_assertions = "1"

Cargo 命令

cargo new 创建二进制项目(--lib 用于库)。build 编译到 target/。--release 启用优化。check 比 build 快(无代码生成)。clippy 捕获常见错误。fmt 格式化代码。doc 生成 HTML 文档。add 插入依赖。

rust
cargo new myapp        # Create new project
cargo build            # Compile
cargo build --release  # Optimized build
cargo run              # Build and run
cargo test             # Run tests
cargo check            # Fast type-check
cargo fmt              # Format code
cargo clippy           # Lint
cargo doc --open       # Generate docs
cargo add serde        # Add dependency

工作空间

工作空间将共享目标目录和 Cargo.lock 的多个 crate 分组。成员是单独的 crate。[workspace.dependencies] 集中依赖版本。每个 crate 用 workspace = true 引用它们。由于共享编译而构建更快。被 rust-analyzer 等大型项目使用。

rust
# Root Cargo.toml
[workspace]
members = ["crate-a", "crate-b"]

# Shared dependencies
[workspace.dependencies]
serde = "1.0"

# In crate-a/Cargo.toml
[dependencies]
serde = { workspace = true }

Feature

Feature 启用条件编译。除非 --no-default-features,否则启用默认 feature。cfg(feature = ...) 门控代码。依赖中的 dep: 语法避免 feature 统一。适用于可选功能和平台特定代码。Crate 可以向消费者暴露 feature。

rust
# Cargo.toml
[features]
default = ["csv"]
csv = ["dep:csv-parse"]
json = ["dep:serde_json"]

# Conditional compilation
#[cfg(feature = "csv")]
pub fn parse_csv() { /* ... */ }

发布

crates.io 是 Rust 包注册表。cargo login 进行身份验证。--dry-run 捕获问题。一旦发布,版本不能重新发布(yank 仅从搜索中隐藏)。遵循 semver:patch 用于修复,minor 用于功能,major 用于破坏性更改。README 和 license 是必需的。

rust
# Login (one time)
cargo login <token>

# Check before publishing
cargo publish --dry-run

# Publish to crates.io
cargo publish

# Version bumping
cargo bump patch  # 0.1.0 -> 0.1.1
cargo bump minor  # 0.1.0 -> 0.2.0
cargo bump major  # 0.1.0 -> 1.0.0
26

测试 Rust

单元测试

测试放在 #[cfg(test)] 模块中。use super::* 导入父模块。#[test] 标记测试函数。assert_eq! 检查相等性。#[should_panic] 期望 panic。用 cargo test 运行测试。单元测试与代码放在一起。cfg(test) 属性确保测试不会在 release 构建中编译。

rust
pub fn add(a: i32, b: i32) -> i32 { a + b }

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
    }
    #[test]
    #[should_panic]
    fn test_panic() {
        panic!("expected");
    }
}

集成测试

集成测试放在 tests/ 目录中。每个文件作为单独的 crate 编译。它们只能测试公共 API。适用于端到端测试。用 --test <name> 运行特定测试。集成测试编译较慢但测试真实接口。

rust
// tests/integration_test.rs
use myapp::add;

#[test]
fn test_add_integration() {
    assert_eq!(add(2, 3), 5);
}
// Run: cargo test --test integration_test
// Each file in tests/ is a separate crate

测试组织

#[ignore] 跳过测试,除非传递 --ignored。按名称模式过滤测试。--nocapture 显示 println! 输出。测试默认并行运行。使用 --test-threads=1 进行顺序运行。自定义测试框架可以替换默认测试运行器。适用于基准测试和属性测试。

rust
#[test]
fn it_works() { /* ... */ }

// Custom test harness
#[test]
#[ignore = "slow test"]
fn slow_test() { /* ... */ }

// Run only ignored tests
cargo test -- --ignored

// Filter by name
cargo test test_add

// Show output
cargo test -- --nocapture

断言

assert! 检查布尔值。assert_eq!/assert_ne! 比较值,失败时显示调试输出。自定义消息有助于调试。对于浮点数,使用 approx crate。对于部分相等,实现 PartialEq。断言失败时调试输出显示两个值。

rust
assert!(true);                          // Boolean
assert_eq!(2 + 2, 4);                   // Equality
assert_ne!(3, 4);                       // Inequality
assert!(x > 0, "x must be positive");   // Custom message
assert_eq!(a, b, "got {}, expected {}", a, b);
// Debug output on failure
assert_eq!(vec![1, 2], vec![1, 2]);

属性测试

proptest 生成随机输入以查找失败案例。策略(a in range)定义输入生成器。prop_assert! 报告失败并带有最小反例。缩减找到最小的失败输入。比手写测试更适合边缘情况。类似于 Haskell 中的 QuickCheck。

rust
// Cargo.toml: proptest = "1"
use proptest::prelude::*;

proptest! {
    #[test]
    fn test_add_commutative(a in -1000..1000, b in -1000..1000) {
        prop_assert_eq!(add(a, b), add(b, a));
    }
    #[test]
    fn test_string_len(s in ".{0,100}") {
        prop_assert!(s.len() <= 100);
    }
}

这篇内容对您有帮助吗?

学习路径

从零开始学习

通过结构化课程从头学习这个语言。