本工具使用的開源套件

本工具程式碼中捆綁了 1 個開源套件。

Rust 速查 — 簡明參考

Rust 2021 版語法、所有權、錯誤處理與最常用 std API 速查手冊,覆蓋約 80% 日常場景。

Rs

Rust Rust 2021 edition

rustc / cargo · 編譯型 · 系統級 · 函數式 · 併發 · 靜態 · 強類型 · affine(移動語義)

學習路徑

先學會 cargo new/run 與 println! → 掌握變量綁定、所有權與借用(Rust 核心)→ 深入 match、函數與閉包 → 用 Vec/HashMap/迭代器組織數據 → 理解 trait 與泛型 → 用 Result 與 ? 處理錯誤 → 用線程與 channel 寫併發 → 再按需學網絡、時間與構建測試。FAQ 節適合回頭避坑。

1.Hello World 與構建環境

運行 Rust 程序、cargo 項目與工具鏈。

最小程序

fn main 是入口。println! 宏輸出。語句以分號結尾。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 新建項目:
// cargo new hello
// cd hello
// 編輯 src/main.rs:
fn main() {
println!("Hello, world!");
}
// 運行:
// cargo run
// 輸出:Hello, world!
// 要點:
// fn 關鍵字定義函數
// println! 是宏(帶 !)
// main 返回 () 類型
// 表達式末尾分號可選(見函數節)

運行與構建

cargo run 編譯並運行、cargo build 生成二進制、cargo check 快速檢查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 編譯並運行:
// cargo run
// 構建(調試):
// cargo build
// 快速檢查(不生成二進制):
// cargo check
// 發佈構建(優化):
// cargo build --release
// 產物位置:
// target/debug/hello
// target/release/hello
// 單文件直接編譯:
// rustc main.rs
// 生成可執行文件 main
// 大項目始終用 cargo

cargo 項目

cargo new 初始化項目。Cargo.toml 聲明包與依賴。src/ 放源碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 創建二進制項目:
// cargo new hello --bin
// 創建庫項目:
// cargo new mylib --lib
// Cargo.toml 核心:
// [package]
// name = "hello"
// version = "0.1.0"
// edition = "2021"
// 結構:
// Cargo.toml 清單
// src/main.rs 入口
// src/lib.rs 庫入口
// target/ 構建產物
// 文檔生成:
// cargo doc --open
// 構建緩存 target 可刪

edition

edition 聲明語言版本。2021 是當前版本。rust-version 聲明最低工具鏈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Cargo.toml:
// [package]
// name = "app"
// version = "0.1.0"
// edition = "2021" // 語言版
// rust-version = "1.70" // 最低 rustc
// 查看當前版本:
// rustc --version
// 升級 edition:
// cargo fix --edition
// 各版本:
// 2015(舊)/ 2018 / 2021
// edition 差異:
// 關鍵字的可用性
// 宏與預導入差異
// 同一項目一次只用一種

輸出宏

println!/print! 輸出、eprintln! 錯誤流、format! 生成字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
let name = "Rust";
let ver = 1.82;
// 佔位符:
println!("Hello, {}!", name);
println!("版本: {}", ver);
// 編號與命名:
println!("{0} {1} {0}", "a", "b");
println!("{name} v{ver}", name = name, ver = ver);
// 格式控制:
println!("{:>10}", "右對齊"); // 寬度
println!("{:.2}", 3.14159); // 精度
println!("{:b}", 10); // 二進制
println!("{:?}", vec![1, 2]); // Debug
// 無換行:
print!("連續");
// 錯誤流:
eprintln!("出錯了");
}

命令行參數

std::env::args 獲取命令行參數。第一個是程序路徑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use std::env;
fn main() {
// 收集參數:
let args: Vec<String> = env::args().collect();
println!("共 {} 個參數", args.len());
for a in &args {
println!("{}", a);
}
// 業務參數從第 1 個開始:
// args[0] 是程序路徑
// 忽略非法 UTF-8 用 args_os:
// env::args_os()
// 常見用法:
// let name = args.get(1).unwrap();
// 複雜解析用 clap 庫
}

多文件

mod 聲明模塊。子模塊文件或目錄。use 導入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/main.rs:
mod math; // 聲明模塊
use math::add;
fn main() {
println!("{}", add(1, 2));
}
// src/math.rs:
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// 或目錄:
// src/math/mod.rs
// src/math/utils.rs
// 導出:
// pub fn / pub struct
// 私有默認:
// 同模塊可訪問
// 子模塊用 super:: 引用父級

直接編譯

rustc 編譯單個文件。無 cargo 依賴。適合練習與小腳本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 編譯:
// rustc hello.rs
// 運行:
// ./hello
// 帶輸出名:
// rustc hello.rs -o app
// 優化:
// rustc -O hello.rs
// 調試信息:
// rustc -g hello.rs
// 警告全開:
// rustc -D warnings hello.rs
// 查看版本:
// rustc --version
// 限制:
// 無依賴管理
// 標準庫外的 crate
// 需 cargo 處理
// 適合教學與單文件實驗

2.變量與綁定

let 綁定、可變性、遮蔽、作用域與類型。

let 與 mut

let 綁定變量默認不可變。mut 聲明可變。Rust 變量默認不可變。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
// 不可變綁定:
let x = 5;
// x = 6; // 錯誤:不可變
// 可變綁定:
let mut y = 5;
y = 6; // 允許
println!("{}", y);
// 顯式類型:
let n: u32 = 42;
// 先聲明後賦值:
let mut z;
z = 10;
// 未使用警告:
// 用 _ 前綴忽略
let _unused = 0;
}

遮蔽

同名 let 遮蔽舊綁定。可改變類型。不同於 mut 修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
let x = 5;
let x = x + 1; // 遮蔽
let x = x * 2;
println!("{}", x); // 12
// 改變類型:
let spaces = " ";
let spaces = spaces.len(); // usize
// 與 mut 區別:
// 遮蔽是新建綁定
// mut 是修改原值
// 遮蔽後舊值不可訪問
// 作用域內遮蔽:
{
let x = 99; // 內層遮蔽
println!("{}", x); // 99
}
println!("{}", x); // 12
}

const 與 static

const 編譯期常量。static 靜態變量。命名用全大寫。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 常量:
const MAX_SIZE: u32 = 100;
const PI: f64 = 3.141592;
// 靜態變量(有固定地址):
static APP_NAME: &str = "demo";
// 靜態可變(unsafe):
static mut COUNTER: u32 = 0;
fn inc() {
unsafe {
COUNTER += 1; // 需 unsafe
}
}
// 區別:
// const 編譯期內聯,無地址
// static 有固定內存地址
// static mut 訪問需 unsafe
// 現代做法:
// 優先 const
// 全局狀態用互斥鎖
// 常量表達式可計算

類型推斷

編譯器根據上下文推斷類型。必要時顯式標註。整數默認 i32。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fn main() {
// 自動推斷:
let x = 42; // i32(默認)
let y = 3.14; // f64(默認)
// 上下文推斷:
let mut v = Vec::new();
v.push(1); // 推斷 Vec<i32>
// 顯式標註:
let n: u64 = 42;
let f: f32 = 3.14;
// 方法返回值定類型:
let s: u32 = "42".parse().unwrap();
// 推斷失敗需標註
// 整數類型默認 i32
// 浮點默認 f64

解構綁定

let 支持模式解構。元組、結構體、數組一次性取出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
// 元組解構:
let (a, b, c) = (1, 2, 3);
println!("{} {} {}", a, b, c);
// 數組解構:
let [x, y, _] = [10, 20, 30];
// 結構體解構:
struct Point { x: i32, y: i32 }
let p = Point { x: 1, y: 2 };
let Point { x, y } = p;
// 重命名:
let Point { x: px, y: py } = p;
// 剩餘:
let (first, rest..) = (1, 2, 3, 4);
// 通配符 _ 忽略:
let (_, b2) = (1, 2);
}

作用域

塊作用域 {}。變量生命週期。內層可訪問外層。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fn main() {
let outer = 1;
{
// 內層可訪問外層:
println!("{}", outer);
let inner = 2;
println!("{}", inner);
}
// println!("{}", inner); // 錯誤:已離開作用域
// 同作用域內不能同名:
// 遮蔽需嵌套作用域
// 所有權隨作用域結束
// 釋放順序(逆序):
// Drop 在作用域結束時調用
// 提前結束用 drop()

命名規範

Rust 命名約定。snake_case 變量/函數、CamelCase 類型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 變量/函數:snake_case
let user_name = "nick";
fn get_user() {}
// 類型/枚舉/trait:CamelCase
struct UserAccount {}
enum ColorChoice {}
trait Displayable {}
// 常量/靜態:SCREAMING_SNAKE
const MAX_ITEMS: usize = 100;
// 私有標識可加 _ 前綴
// 泛型參數:單大寫字母 T
// 生命週期:'a 'b
// 內置風格:
// is_ / has_ 前綴布爾函數
// into_ / to_ / as_ 轉換
// 編譯器 lint 檢查命名

類型轉換

as 做顯式數值轉換。From/Into trait 做安全轉換。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
// as 數值轉換:
let i: i32 = 42;
let u: u64 = i as u64;
let f: f64 = i as f64;
// 截斷風險:
let big: u16 = 300;
let small: u8 = big as u8; // 44
// From/Into:
let s = String::from("hello");
let s2: String = "hi".into();
// 精確轉換:
let x: u8 = 255;
let y: u32 = x.into(); // From<u8> for u32
// 字符串解析:
let n: i32 = "42".parse().unwrap();
// 顯示要求:
// let n: i32 = "42".parse()?;

3.類型系統

基礎類型、元組、結構體與枚舉。

基礎類型

整數、浮點、布爾、字符。標量類型與長度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
// 整數:
let i: i32 = -10; // 有符號
let u: u32 = 10; // 無符號
// 浮點:
let f: f32 = 1.5;
let d: f64 = 3.14159;
// 布爾:
let b: bool = true;
let c: bool = false;
// 字符(Unicode):
let ch: char = '中';
let emoji: char = '🚀';
// 字符串片段:
let s: &str = "text";
// 整數默認 i32,浮點默認 f64
// char 佔 4 字節(Unicode 標量)

整數類型

i8-u128、usize/isize。有符號無符號。平台相關。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn main() {
// 有符號:i8 i16 i32 i64 i128
// 無符號:u8 u16 u32 u64 u128
// 平台相關:usize isize
let a: i8 = -128; // 範圍 -128..127
let b: u8 = 255; // 範圍 0..255
let c: i64 = 9_223_372_036_854_775_807;
// 字面量後綴:
let d = 255u8;
let e = 1_000_000i64; // 下劃線分隔
// 十六進制/二進制:
let h = 0xff;
let bin = 0b1010;
// usize 用途:
// 索引、長度、指針大小
// 溢出檢查(調試構建 panic)

浮點數

f32/f64 符合 IEEE 754。NaN、無窮、運算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
let a: f64 = 3.14;
let b: f64 = 2.0;
// 運算:
let sum = a + b;
let div = a / b;
let pow = a.powi(2); // 冪
let sqrt = a.sqrt();
// 特殊值:
let inf = f64::INFINITY;
let neg_inf = f64::NEG_INFINITY;
let nan = f64::NAN;
// 判斷:
// nan.is_nan()
// inf.is_infinite()
// 注意:
// NaN != NaN
// 浮點不適合精確比較
// 精度:
// f32 約 7 位十進制
// f64 約 15-16 位

char 類型

char 是 Unicode 標量值,佔 4 字節。單引號字面量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
let c = 'A';
let zh = '中';
let emoji = '😀';
// 轉義:
let tab = '\t';
let quote = '\'';
let newline = '\n';
// Unicode 碼點:
let cp = '\u{1F600}'; // 😀
// 操作:
println!("{}", c.is_ascii());
println!("{}", zh.is_alphabetic());
// 與數字:
let n = '9' as u8; // 57
// char 與 String:
// 字符轉字符串:
let s = c.to_string();
// 一個 char 不是 &str

元組

固定長度異構集合。索引訪問。解構。空元組是 ()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
// 定義:
let t = (1, "two", 3.0);
// 索引訪問:
println!("{}", t.0); // 1
println!("{}", t.1); // "two"
// 類型標註:
let t2: (i32, bool) = (5, true);
// 解構:
let (a, b, c) = t;
// 單元素元組:
let one = (42,); // 注意逗號
// 空元組/單元類型:
let u: () = ();
// () 是函數默認返回
// 常用:
// 多值返回 (val, err)
// 交換: (a, b) = (b, a)

結構體

struct 定義命名字段。實例化、訪問、更新語法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct User {
name: String,
age: u8,
active: bool,
}
fn main() {
// 實例化:
let mut u = User {
name: String::from("nick"),
age: 30,
active: true,
};
// 訪問字段:
println!("{}", u.name);
u.age += 1; // 需 mut
// 更新語法:
let u2 = User { age: 20, ..u };
// 元組結構體:
struct Point(i32, i32);
let p = Point(1, 2);
println!("{}", p.0);
// 單元結構體:
struct Unit;
}

枚舉

enum 定義多種變體。可攜帶數據。match 窮盡處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum Message {
Quit,
Move { x: i32, y: i32 }, // 結構體變體
Write(String), // 元組變體
ChangeColor(u8, u8, u8),
}
fn main() {
let m = Message::Write(String::from("hi"));
// match 窮盡匹配:
match m {
Message::Quit => println!("退出"),
Message::Move { x, y } => {
println!("移動 {} {}", x, y);
}
Message::Write(s) => println!("{}", s),
Message::ChangeColor(r, g, b) => {
println!("顏色 {}-{}-{}", r, g, b);
}
}
// 枚舉可帶泛型/方法
// Option/Result 也是枚舉

單元類型

() 表示沒有值。函數默認返回 ()。無參無返回的約定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
// () 類型:
let unit: () = ();
// 函數默認返回 ():
fn say() {}
// 等同:
fn say2() -> () {}
// 表達式返回 ():
let r = { let x = 1; }; // 空塊
// 用法:
// 作為類型參數:
// Result<T, ()> 只關心錯誤
// HashMap<K, ()> 當集合
// 泛型佔位:
// 不關心具體類型
// 打印 Debug:
println!("{:?}", ());

4.所有權與引用

所有權、借用、引用與切片。Rust 內存安全核心。

所有權

每個值只有一個所有者。離開作用域自動釋放。移動而非拷貝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn main() {
// 所有權規則:
// 1. 每個值只有一個所有者
// 2. 所有者離開作用域釋放
// 3. 轉移所有權叫移動
let s1 = String::from("hello");
let s2 = s1; // 移動:s1 失效
// println!("{}", s1); // 錯誤:已移動
println!("{}", s2);
// 拷貝類型(棧上 Copy):
let a = 5;
let b = a; // 拷貝,都有效
println!("{} {}", a, b);
// 函數傳參也轉移:
// fn take(s: String) {}
// 取回用返回值

引用

&T 不可變引用,不轉移所有權。借用後原值仍可用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
let s = String::from("hello");
// 不可變引用:
let len = calculate_length(&s);
println!("{}", len); // 5
println!("{}", s); // 仍可用
}
fn calculate_length(s: &String) -> usize {
s.len() // 借用,不擁有
}
// 引用規則:
// &T 只讀借用
// 引用不轉移所有權
// 引用默認不可變
// 多個不可變引用可並存
// 引用離開作用域不釋放值
// 常見:
// 參數用 &str / &T 避免拷貝

可變引用

&mut T 可變引用。同一作用域一個變量只能有一個可變引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s); // hello!
}
fn change(s: &mut String) {
s.push_str("!");
}
// 規則:
// 可變引用 &mut T
// 同一作用域只有一個
// 不可變與可變不能並存:
// let r1 = &s;
// let r2 = &mut s; // 錯誤
// 數據競爭編譯期禁止:
// 讀多寫少場景
// 借用結束後才可再借
// 花括號可縮短借用範圍

解引用

* 運算符訪問引用指向的值。解引用與引用配對。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
let x = 5;
let r = &x; // 引用
// 解引用訪問值:
println!("{}", *r);
// 解引用賦值:
let mut y = 5;
let r2 = &mut y;
*r2 += 1; // 修改 y
println!("{}", y); // 6
// 方法調用自動解引用:
// s.len() 無需 (*s).len()
// 索引底層也是解引用
// 引用比較:
// == 比較值還是引用?
// 常比較解引用後的值
// 懸掛引用編譯器拒絕

生命週期

'a 標註引用有效範圍。借用檢查器保證引用有效。省略規則。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 生命週期標註:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
fn main() {
let s1 = String::from("long");
let s2 = String::from("s");
let r = longest(&s1, &s2);
println!("{}", r);
}
// 規則:
// 'a 表示引用的有效範圍
// 返回值生命週期取輸入交集
// 結構體可存引用:
// struct X<'a> { part: &'a str }
// 省略規則:
// 單輸入省略為輸出
// 方法省略 self 生命週期
// 多數情況無需顯式標註

切片

&[T] 是連續元素的引用。字符串切片 &str。無所有權。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn main() {
// 數組切片:
let arr = [1, 2, 3, 4, 5];
let s: &[i32] = &arr[1..4]; // [2,3,4]
println!("{:?}", s);
// 字符串切片:
let st = String::from("hello");
let part: &str = &st[0..2]; // "he"
// 邊界在字符邊界,否則 panic
// 全量切片:
let all = &arr[..];
// 常見:
// 函數參數用 &[T] 而非 Vec
// &str 是 String 的借用
// 切片長度編譯期未知
// 切片傳引用不拷貝

Deref 自動解引用

Deref trait 讓 &String 自動變 &str 等。參數多態便捷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use std::ops::Deref;
fn main() {
let s = String::from("hello");
// &String 自動變 &str:
let t: &str = &s;
// 函數參數自動轉換:
fn takes_str(s: &str) {}
let s2 = String::from("world");
takes_str(&s2); // 自動解引用
// 自定義 Deref:
struct MyBox<T>(T);
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
// 方法調用也自動:
// my_box.len() 會逐層 deref
// 轉換鏈:&MyBox<T> -> &T

Copy 類型

Copy trait 類型賦值是複製而非移動。整數、浮點、bool、char 等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
// Copy 類型:賦值後仍可用
let a = 5;
let b = a; // 複製
println!("{} {}", a, b); // 都可
// Copy 類型清單:
// 所有整數/浮點/bool/char
// 元組(元素全 Copy)
// 數組
// &T 引用(是 Copy)
// 非 Copy:
// String、Vec、Box
// 結構體默認非 Copy:
#[derive(Clone, Copy)]
struct Point { x: i32, y: i32 }
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // Copy
println!("{} {}", p1.x, p2.x);
// 判斷:
// 離開作用域無 Drop 才能 Copy
// 克隆需要顯式 .clone()

5.流程控制

if、match、循環。Rust 控制流是表達式。

if 表達式

if/else 是表達式,可返回值。條件無需括號。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
let n = 7;
// 分支:
if n > 0 {
println!("正數");
} else if n == 0 {
println!("零");
} else {
println!("負數");
}
// if 是表達式:
let sign = if n > 0 { "+" } else { "-" };
println!("{}", sign);
// 條件不需要括號
// 必須 bool 類型
// 分支返回類型必須一致
// 無 else 的 if 返回 ()

match

match 窮盡匹配。每個分支是表達式。可匹配模式、範圍、守衞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
let x = 3;
// 匹配字面值:
match x {
1 => println!("一"),
2 | 3 => println!("二或三"), // 或
4..=6 => println!("四到六"), // 範圍
_ => println!("其他"), // 通配
}
// 綁定與守衞:
let num = Some(10);
match num {
Some(n) if n > 5 => println!("大數 {}", n),
Some(n) => println!("小數 {}", n),
None => println!("無"),
}
// 必須窮盡(無 _ 會編譯錯誤)
// match 可返回表達式值

while 循環

while 條件循環。條件為真時反覆執行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
let mut n = 3;
// 計數循環:
while n > 0 {
println!("{}", n);
n -= 1;
}
// 條件循環:
let mut line = String::new();
while line.trim() != "quit" {
line.clear();
// 讀取輸入...
break; // 示例直接跳出
}
// while 循環沒有索引
// 遍歷集合優先 for
// 需索引場景用:
// 計數器 + while
// while 返回 ()

for 循環

for 遍歷範圍與迭代器。Rust 首選循環方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn main() {
// 範圍:
for i in 0..5 {
println!("{}", i); // 0..4
}
// 含端點:
for i in 0..=5 {
println!("{}", i); // 0..5
}
// 遍歷數組:
let arr = [10, 20, 30];
for v in &arr {
println!("{}", v);
}
// 需索引:
for (i, v) in arr.iter().enumerate() {
println!("{}: {}", i, v);
}
// 倒序:
for i in (0..3).rev() {}
// 遍歷集合不轉移所有權
// 移動遍歷用 into_iter

無限循環

loop 無限循環直到 break。可返回值。可帶標籤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn main() {
// 無限循環:
let mut count = 0;
let result = loop {
count += 1;
if count == 3 {
break count * 10; // 返回 30
}
};
println!("{}", result);
// 標籤跳出外層:
'outer: loop {
loop {
break 'outer; // 跳出外層
}
}
// 用途:
// 重試、輪詢、事件循環
// 與 while true 等同
// loop 更慣用可返回值

break 與 continue

break 退出循環。continue 跳過本輪。可配合標籤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fn main() {
let mut n = 0;
loop {
n += 1;
// 跳過偶數:
if n % 2 == 0 {
continue;
}
if n > 7 {
break;
}
println!("{}", n); // 1 3 5 7
}
// continue 跳出當前迭代
// break 帶值:
let v = loop { break 42; };
// 嵌套標籤:
'a: for i in 0..3 {
for j in 0..3 {
if i + j == 2 {
continue 'a; // 繼續外層
}
}
}
}

if let

if let 簡化單模式匹配。只關心一種變體時使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
let opt = Some(5);
// 匹配單模式:
if let Some(v) = opt {
println!("值 {}", v);
}
// 帶 else:
if let Some(v) = opt {
println!("有值 {}", v);
} else {
println!("無值");
}
// 其他模式:
let res: Result<i32, &str> = Ok(10);
if let Ok(v) = res {
println!("成功 {}", v);
}
// 對應 match:
// match opt {
// Some(v) => ...,
// _ => (),
// }
// 更簡潔、只關注一種情形
// 非窮盡、可組合

while let

while let 循環直到模式不再匹配。迭代處理集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn main() {
let mut stack = vec![1, 2, 3];
// 不斷彈出直到空:
while let Some(top) = stack.pop() {
println!("{}", top); // 3 2 1
}
// 處理字符序列:
let mut chars = "abc".chars();
while let Some(c) = chars.next() {
println!("{}", c);
}
// 對應 loop + match:
// loop {
// match v.next() {
// Some(x) => ...
// None => break,
// }
// }
// 迭代器可迭代對象
// 條件複雜時用 loop

6.函數

函數定義、返回值、所有權傳遞、閉包與泛型。

函數定義

fn 定義函數。參數需類型標註。函數體是表達式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 無返回值:
fn greet(name: &str) {
println!("你好 {}", name);
}
// 有返回值:
fn add(a: i32, b: i32) -> i32 {
a + b // 最後一個表達式
}
// 顯式 return:
fn early(x: i32) -> i32 {
if x < 0 {
return 0;
}
x * 2
}
fn main() {
greet("Rust");
println!("{}", add(1, 2));
println!("{}", early(-5));
}
// 返回 () 可省略 ->

返回值

最後一個表達式是返回值。分號區分語句與表達式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
let x = five();
println!("{}", x); // 5
let y = with_semi();
println!("{:?}", y); // ()
}
fn five() -> i32 {
5 // 表達式,返回值
}
fn with_semi() {
5; // 語句,返回 ()
}
// 要點:
// 表達式 = 有值的代碼
// 語句 = 以分號結尾
// 無分號 = 返回該值
// 塊是表達式:
let n = { let a = 1; a + 2 }; // 3
// if/match/loop 都是表達式

參數

參數默認不可變。模式解構參數。引用參數避免拷貝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 引用參數:
fn show(name: &str) {
println!("{}", name);
}
// 可變參數需 mut:
fn append(v: &mut Vec<i32>) {
v.push(1);
}
// 元組解構參數:
fn sum((a, b): (i32, i32)) -> i32 {
a + b
}
fn main() {
show("nick");
let mut v = vec![];
append(&mut v);
println!("{}", sum((1, 2)));
}
// 參數是值的拷貝/移動
// 大類型傳引用
// 需要修改傳 &mut
// 返回新值避免引用

所有權參數

傳值即轉移所有權。借用可複用。需要時返回所有權。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn takes(s: String) {
println!("{}", s); // s 在此被使用後釋放
} // s 離開作用域,釋放
fn main() {
let s = String::from("hi");
takes(s);
// println!("{}", s); // 錯誤:已移動
// 取回所有權:
let s2 = String::from("keep");
let s2 = give_back(s2);
println!("{}", s2);
}
fn give_back(s: String) -> String {
s // 返回轉移回來
}
// 替代:
// 借用 &str 通常更合適
// 需要所有權才傳值
// 返回多個值用元組

借用參數

&T 只讀借用、&mut T 可變借用。避免轉移所有權。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 只讀借用:
fn len(s: &str) -> usize {
s.len()
}
// 多個只讀借用可並存:
fn print_two(a: &str, b: &str) {
println!("{} {}", a, b);
}
// 可變借用:
fn push_item(v: &mut Vec<i32>, x: i32) {
v.push(x);
}
fn main() {
let s = String::from("abc");
println!("{}", len(&s));
println!("{}", s); // 借用後仍可用
let mut v = vec![1];
push_item(&mut v, 2);
println!("{:?}", v);
}
// &str 參數比 &String 更通用

閉包

閉包捕獲環境變量。|參數| 語法。可存變量、傳函數。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn main() {
// 定義:
let add = |a: i32, b: i32| a + b;
println!("{}", add(1, 2));
// 捕獲外部變量:
let factor = 3;
let mul = |x: i32| x * factor;
println!("{}", mul(5)); // 15
// 閉包可修改捕獲(需 mut):
let mut count = 0;
let mut inc = || {
count += 1;
count
};
println!("{}", inc()); // 1
// 作為參數:
let evens: Vec<i32> =
(1..=6).filter(|x| x % 2 == 0).collect();
println!("{:?}", evens);
// 移入 move 閉包見併發節

函數指針

fn 類型引用普通函數。fn(T) -> U 語法。閉包轉 fn。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn add_one(x: i32) -> i32 {
x + 1
}
// 函數指針類型:
fn apply(f: fn(i32) -> i32, x: i32) -> i32 {
f(x)
}
fn main() {
let f: fn(i32) -> i32 = add_one;
println!("{}", f(10)); // 11
println!("{}", apply(add_one, 10));
// 不捕獲的閉包可轉 fn:
let c = |x: i32| x * 2;
let g: fn(i32) -> i32 = c;
println!("{}", g(5));
// 函數數組/向量:
let funcs = [add_one, add_one];
// 泛型場景用 Fn trait

泛型函數

泛型參數 <T> 抽象類型。可加約束。類型推斷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 泛型函數:
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut max = &list[0];
for item in list {
if item > max {
max = item;
}
}
max
}
fn main() {
let nums = vec![34, 50, 25];
println!("{}", largest(&nums));
let chars = vec!['y', 'm', 'a'];
println!("{}", largest(&chars));
}
// T: PartialOrd 是 trait 約束
// 多個參數:fn pair<T, U>
// 調用時自動推斷類型
// 也可顯式:largest::<i32>(&nums)
// 泛型不損失性能(單態化)

7.字符串

&str 與 String、操作、格式化、解析與遍歷。

&str 與 String

&str 是借用字符串,String 是擁有所有權的可增長字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
// &str 字符串字面量:
let s: &str = "hello";
// String 擁有所有權:
let mut owned = String::from("hello");
owned.push_str(" world");
// &str -> String:
let a: String = s.to_string();
let b: String = String::from(s);
// String -> &str(借用):
let c: &str = &owned;
let d: &str = owned.as_str();
// 區別:
// &str 固定長度,不擁有數據
// String 可增長,堆上分配
// 參數優先用 &str
// 需要擁有再轉 String

String 操作

追加、插入、替換、刪除。修改 String 的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
let mut s = String::from("hello");
// 追加:
s.push_str(", world"); // 字符串
s.push('!'); // 單個字符
// 插入:
s.insert(5, ' ');
// 替換:
let t = s.replace("hello", "hi");
// 刪除:
s.pop(); // 移除末尾
s.truncate(5); // 截斷
// 清空:
s.clear();
// 拼接:
let s1 = String::from("a");
let s2 = String::from("b");
let s3 = s1 + &s2; // s1 被移動
let s4 = format!("{} {}", s3, "c");
// 長度:s.len() 字節數
}

格式化

format! 拼接字符串。佔位符與格式控制。安全高效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn main() {
let name = "Nick";
let age = 30;
// 基礎拼接:
let s = format!("{} 今年 {} 歲", name, age);
println!("{}", s);
// 編號/命名:
format!("{0} {1} {0}", "a", "b");
format!("{name}-{age}", name = name, age = age);
// 對齊與寬度:
format!("{:>8}", "right"); // 右對齊
format!("{:<8}", "left"); // 左對齊
format!("{:^8}", "center");
// 數字格式:
format!("{:.2}", 3.14159); // 3.14
format!("{:+}", 42); // +42
format!("{:08b}", 10); // 二進制補零
format!("{:#x}", 255); // 0xff
// Debug 調試格式:
format!("{:?}", vec![1, 2]);

字符串切片

&s[a..b] 按字節切片。必須在字符邊界,否則 panic。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
let s = String::from("hello 世界");
// 字節切片:
let part = &s[0..5]; // "hello"
println!("{}", part);
// 字符邊界:
// "世界" 每個字符 3 字節
let w = &s[6..12]; // "世界"
// 錯誤的邊界 panic:
// let bad = &s[0..6]; // 非字符邊界
// 安全遍歷方式:
for c in s.chars() {
println!("{}", c);
}
// 取單個字符:
let first = s.chars().next();
// 按行:
for line in s.lines() {}
// 索引是字節位置,非字符位置

字符串遍歷

chars() 按字符、bytes() 按字節、char_indices 帶位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
fn main() {
let s = "你好Rust";
// 按字符遍歷:
for c in s.chars() {
println!("{}", c);
}
// 按字節遍歷:
for b in s.bytes() {
println!("{:02x}", b);
}
// 字符 + 字節偏移:
for (i, c) in s.char_indices() {
println!("{} at {}", c, i);
}
// 按行:
for line in "a\nb\n".lines() {
println!("<{}", line);
}
// 分割:
for part in "a,b,c".split(",") {
println!("{}", part);
}
// 統計字符數:
let n = s.chars().count();
println!("{} 個字符", n);
// 字節數 len() 不等於字符數

字符串解析

str::parse 解析為數值。parse::<T>() 需標註目標類型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
// 解析為 i32:
let n: i32 = "42".parse().unwrap();
// 顯式泛型:
let f: f64 = "3.14".parse().unwrap();
let u: u32 = "255".parse().unwrap();
// 錯誤處理:
let result = "abc".parse::<i32>();
match result {
Ok(v) => println!("{}", v),
Err(e) => println!("解析失敗: {}", e),
}
// 布爾:
let b: bool = "true".parse().unwrap();
// 數字轉字符串:
let s = 42.to_string();
let s2 = format!("{}", 3.14);
// ? 簡化:
// fn f() -> Result<i32, ParseIntError> {
// Ok("42".parse()?)
// }

字符操作

char 方法:大小寫、數字判斷、空白。字符串方法組合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn main() {
let c = 'a';
// 判斷:
println!("{}", c.is_alphabetic());
println!("{}", c.is_numeric());
println!("{}", c.is_whitespace());
println!("{}", c.is_ascii());
// 大小寫:
let up = 'a'.to_uppercase().to_string();
let low = 'A'.to_lowercase().to_string();
// 字符串方法:
let s = "Hello, Rust!";
s.to_lowercase();
s.to_uppercase();
s.trim(); // 去空白
s.starts_with("Hello");
s.contains("Rust");
s.replace("Rust", "Go");
// 子串:
let sub = &s[7..]; // "Rust!"
// 檢查空:s.is_empty()
// 重複:"ab".repeat(3)

字符串方法

查詢、修剪、分割、判斷。常用 str 方法鏈式調用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
let s = " Hello, Rust 2024 ";
// 修剪與大小寫:
let t = s.trim();
let lower = t.to_lowercase();
let upper = t.to_uppercase();
// 查詢:
t.starts_with("Hello");
t.ends_with("2024");
t.contains("Rust");
t.find("Rust"); // Option<usize>
// 分割:
let parts: Vec<&str> = t.split(",").collect();
let lines: Vec<&str> = t.lines().collect();
let words: Vec<&str> = t.split_whitespace().collect();
// 替換與刪除:
t.replace("Rust", "Go");
t.replacen("o", "0", 1);
// 重複與補全:
"ab".repeat(3);
format!("{:0>5}", 42); // 00042
// 判斷空:
s.is_empty();
}

8.集合

Vec、HashMap、集合類型與迭代器。

Vec

Vec<T> 動態數組。可變長,可增刪。棧上引用、堆上數據。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
// 創建:
let mut v = Vec::new();
v.push(1);
v.push(2);
// 宏創建:
let mut v2 = vec![10, 20, 30];
let zero = vec![0; 5]; // [0,0,0,0,0]
// 訪問:
let a = &v2[0]; // 索引,越界 panic
let b = v2.get(1); // Option,安全
// 修改:
v2[0] = 99;
v2.push(40);
v2.pop(); // 移除末尾
v2.remove(1); // 移除索引
v2.insert(0, 5); // 插入開頭
// 長度:v2.len()
// 清空:v2.clear()
// 遍歷:for x in &v2 {}
// 排序:v2.sort()

Vec 操作

迭代、過濾、映射、收集。切片視圖與所有權。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
let v = vec![1, 2, 3, 4];
// 迭代器轉換:
let doubled: Vec<i32> =
v.iter().map(|x| x * 2).collect();
// 過濾:
let evens: Vec<&i32> =
v.iter().filter(|x| **x % 2 == 0).collect();
// 求和:
let sum: i32 = v.iter().sum();
// 最大值:
let max = v.iter().max();
// 查找:
let found = v.iter().find(|x| **x == 3);
// 遍歷修改:
let mut w = vec![1, 2];
for x in w.iter_mut() {
*x += 10;
}
// 移動遍歷:
let owned: Vec<i32> = w.into_iter()
.map(|x| x + 1).collect();
// 切片視圖:
let part = &v[1..3];

HashMap

HashMap<K, V> 鍵值對。哈希實現,無序。查找 O(1)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::collections::HashMap;
fn main() {
// 創建:
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Red"), 20);
// 宏創建:
let mut m: HashMap<&str, i32> =
HashMap::from([("a", 1), ("b", 2)]);
// 讀取:
let score = scores.get("Blue"); // Option
// 遍歷:
for (k, v) in &scores {
println!("{}: {}", k, v);
}
// 插入或更新:
*m.entry("a").or_insert(0) += 10;
m.insert("c", 3);
// 刪除:
m.remove("a");
// 檢查:
m.contains_key("b");
// 長度:m.len()
// 無序,遍歷順序隨機

HashSet

HashSet<T> 唯一元素集合。去重、交集、並集、差集。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use std::collections::HashSet;
fn main() {
// 創建:
let mut set = HashSet::new();
set.insert(1);
set.insert(2);
set.insert(2); // 重複忽略
println!("{:?}", set); // {1, 2}
// 操作:
set.contains(&1); // true
set.remove(&1);
set.len();
// 集合運算:
let a: HashSet<i32> = [1, 2, 3].into_iter().collect();
let b: HashSet<i32> = [2, 3, 4].into_iter().collect();
// 並集:
let u: HashSet<_> = a.union(&b).cloned().collect();
// 交集:
let i: HashSet<_> = a.intersection(&b).cloned().collect();
// 差集:
let d: HashSet<_> = a.difference(&b).cloned().collect();
// 判斷子集:a.is_subset(&b)

BTree

BTreeMap/BTreeSet 有序鍵。按序遍歷。性能略遜哈希。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::collections::BTreeMap;
fn main() {
// 有序鍵值對:
let mut m = BTreeMap::new();
m.insert("b", 2);
m.insert("a", 1);
m.insert("c", 3);
// 按序遍歷:
for (k, v) in &m {
println!("{}: {}", k, v);
} // a b c 有序
// 區間查詢:
for (_k, v) in m.range("a".."c") {
println!("{}", v);
}
// 最大/最小:
let first = m.first_key_value();
let last = m.last_key_value();
// BTreeSet 同理念:
use std::collections::BTreeSet;
let s: BTreeSet<i32> = [3, 1, 2].into_iter().collect();
println!("{:?}", s); // {1, 2, 3}
// 適用:
// 需要有序遍歷/範圍查詢
// 鍵可比較即可

迭代器

Iterator trait 驅動鏈式操作。惰性求值。消費才執行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn main() {
let v = vec![1, 2, 3, 4, 5];
// 鏈式:
let result: Vec<i32> = v.iter()
.filter(|x| **x % 2 == 1) // 奇數
.map(|x| x * 10)
.collect();
println!("{:?}", result); // [10, 30, 50]
// 惰性:不消費不執行
// 常用適配器:
// .take(n) 取前 n
// .skip(n) 跳過 n
// .rev() 反轉
// .zip(iter) 配對
// .enumerate() 加索引
// 消費方法:
let sum: i32 = v.iter().sum();
let count = v.iter().count();
let any = v.iter().any(|x| x > 3);
let all = v.iter().all(|x| x > 0);
// 數組轉為迭代器:
for x in v.iter() { println!("{}", x); }

VecDeque

VecDeque 雙端隊列。兩端高效增刪。環形緩衝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::collections::VecDeque;
fn main() {
// 創建:
let mut d = VecDeque::new();
// 兩端操作:
d.push_back(1);
d.push_back(2);
d.push_front(0);
println!("{:?}", d); // [0, 1, 2]
d.pop_front(); // 0
d.pop_back(); // 2
// 訪問:
d.front(); // Option
d.back();
d.front_mut();
// 範圍:
d.make_contiguous();
// 適合:
// 隊列/先進先出
// 滑動窗口
// 雙端掃描
// 與 Vec 區別:
// 前端插入 Vec 是 O(n)
// VecDeque 兩端 O(1)
// 隨機索引略慢

嵌套集合

集合的組合。Vec<HashMap>、HashMap 嵌套、多維 Vec。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::collections::HashMap;
fn main() {
// Vec 存結構體:
let users = vec![
("a", 1),
("b", 2),
];
// HashMap 嵌套:
let mut matrix: HashMap<String, Vec<i32>> =
HashMap::new();
matrix.insert("row1".to_string(), vec![1, 2]);
// 插入不存在則新建:
matrix.entry(String::from("row2"))
.or_insert(vec![]).push(3);
// 多維 Vec:
let mut grid = vec![vec![0; 3]; 3];
grid[0][1] = 5;
// 結構體集合:
#[derive(Debug)]
struct Item { id: u32, tags: Vec<String> }
let items = vec![
Item { id: 1, tags: vec!["x".into()] },
];
println!("{:?}", items);
}

9.內存與所有權擴展

堆分配、智能指針與共享所有權。

棧與堆

棧上固定大小、快速。堆上動態大小、需分配。所有權管理生命週期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn main() {
// 棧上數據:
let x = 42; // 固定大小,棧上
// 堆上數據:
let s = String::from("動態大小");
// String 結構在棧,數據在堆
// 自動管理:
// 離開作用域,棧立即彈
// 堆內存由所有權系統釋放
// 無手動 free,無泄漏
// 釋放時機:
// 所有者離開作用域(Drop)
// Box/智能指針控制堆對象
// 遞歸結構需 Box
// 大數組放堆避免棧溢出
}

Box

Box<T> 把值放堆上。大小確定、轉移所有權。遞歸類型必需。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
// 堆上分配:
let b = Box::new(5);
println!("{}", *b); // 解引用
// 遞歸類型必需 Box:
enum List {
Cons(i32, Box<List>),
Nil,
}
let list = List::Cons(1,
Box::new(List::Cons(2, Box::new(List::Nil))));
// Box 只提供單所有權
// 用途:
// 大結構體減少拷貝
// 動態大小 trait 對象
// 遞歸數據結構
// 解引用自動:
// b.len() 自動 deref
// 離開作用域自動釋放

Rc

Rc<T> 引用計數共享所有權。單線程。多個所有者只讀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use std::rc::Rc;
fn main() {
// 共享所有權:
let a = Rc::new(String::from("shared"));
let b = Rc::clone(&a); // 引用計數 +1
let c = Rc::clone(&a);
println!("{}", a);
println!("{}", b);
// 計數:
println!("{}", Rc::strong_count(&a)); // 3
// 只讀共享:
// Rc 中的數據不可變
// 需要可變用 RefCell
// 單線程專用:
// 多線程用 Arc
// 循環引用導致泄漏:
// 用 Weak 打破
// 離開作用域計數減一

RefCell

RefCell<T> 運行時借用檢查。內部可變性。借用衝突 panic。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use std::cell::RefCell;
fn main() {
// 內部可變性:
let cell = RefCell::new(42);
*cell.borrow_mut() += 1; // 修改
println!("{}", cell.borrow());
// 運行時借用規則:
// 一個可變借用 或
// 多個不可變借用
// 衝突:
// let b1 = cell.borrow();
// let b2 = cell.borrow_mut(); // panic!
// 用途:
// 結構體內可變字段
// 配合 Rc 共享可變
// 與 &mut 區別:
// 編譯期 -> 運行時
// 繞開借用檢查器
// 需謹慎,保持借用範圍短

Arc

Arc<T> 線程安全引用計數。多線程共享所有權。原子計數。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use std::sync::Arc;
use std::thread;
fn main() {
// 線程安全共享:
let data = Arc::new(vec![1, 2, 3]);
let mut handles = vec![];
for i in 0..3 {
let d = Arc::clone(&data);
handles.push(thread::spawn(move || {
println!("線程 {}: {:?}", i, d);
}));
}
for h in handles {
h.join().unwrap();
}
// 與 Rc 區別:
// Arc 用原子計數,線程安全
// Rc 單線程
// 只讀共享:
// 可變需 Mutex 配合:
// Arc<Mutex<T>>
// 注意避免循環引用

Mutex

Mutex<T> 互斥訪問內部值。鎖保護。與 Arc 組合共享可變。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// 共享可變狀態:
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let c = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut num = c.lock().unwrap();
*num += 1; // 加鎖修改
}));
}
for h in handles {
h.join().unwrap();
}
println!("{}", *counter.lock().unwrap());
// 鎖生命週期:
// lock() 返回 Guard
// 離開作用域自動解鎖
// 中毒:
// 持鎖 panic 後鎖中毒
// unwrap 處理 or_else
// 多鎖注意死鎖順序

原子類型

AtomicU32 等原子操作。無鎖併發計數。Ordering 內存序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use std::sync::atomic::{
AtomicU32, Ordering
};
use std::thread;
fn main() {
// 原子計數器:
let counter = AtomicU32::new(0);
let mut handles = vec![];
for _ in 0..10 {
let c = &counter;
handles.push(thread::spawn(move || {
c.fetch_add(1, Ordering::SeqCst);
}));
}
for h in handles {
h.join().unwrap();
}
println!("{}", counter.load(Ordering::SeqCst));
// 類型:
// AtomicU8..AtomicI64
// AtomicBool AtomicUsize
// 操作:
// fetch_add / fetch_sub
// compare_exchange
// load / store
// 內存序:
// Relaxed/Release/Acquire
// SeqCst 最嚴格
// 無鎖、不阻塞

智能指針

Deref/Drop trait。Box/Rc/Arc 實現。指針抽象與自動清理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use std::ops::Deref;
// 自定義智能指針:
struct MyBox<T>(T);
// Deref 允許解引用:
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
// Drop 釋放清理:
impl<T> Drop for MyBox<T> {
fn drop(&mut self) {
println!("釋放");
}
}
fn main() {
let m = MyBox(5);
println!("{}", *m); // 解引用 5
// 離開作用域自動 drop
// 標準智能指針:
// Box<T> 堆分配
// Rc<T> 單線程共享
// Arc<T> 多線程共享
// RefCell/Mutex 內部可變
// String/Vec 也是指針+容量

10.trait 與泛型

trait 抽象、實現、泛型約束與多態。

trait 定義

trait 定義共享行為。方法簽名。類似接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 定義 trait:
#[derive(Debug)]
struct Dog;
struct Cat;
// 行為抽象:
trait Speak {
fn sound(&self) -> &'static str;
}
// 為類型實現:
impl Speak for Dog {
fn sound(&self) -> &'static str {
"汪汪"
}
}
impl Speak for Cat {
fn sound(&self) -> &'static str {
"喵"
}
}
fn main() {
let dog = Dog;
let cat = Cat;
println!("{}", dog.sound());
println!("{}", cat.sound());
}
// trait 可含默認實現
// 實現必須滿足方法簽名
// 同一類型可實現多 trait

實現 trait

impl Trait for Type 實現行為。孤兒規則:trait 或類型必須本地。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 為內置類型實現自定義 trait:
trait Greeting {
fn hello(&self) -> String;
}
// 為 &str 實現:
impl Greeting for &str {
fn hello(&self) -> String {
format!("你好,{}", self)
}
}
// 為 Vec 實現:
impl Greeting for Vec<i32> {
fn hello(&self) -> String {
format!("{:?} 長度 {}", self, self.len())
}
}
fn main() {
println!("{}", "Nick".hello());
println!("{}", vec![1, 2].hello());
}
// 孤兒規則:
// trait 或 類型 至少一個本地
// 否則編譯錯誤
// 空實現:
// 標記類型(sealed)
// 泛型實現可複用

默認方法

trait 方法可提供默認實現。覆蓋可選。調用順序自動。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
trait Animal {
fn name(&self) -> String;
// 默認實現:
fn intro(&self) -> String {
format!("我是 {}", self.name())
}
}
struct Dog;
impl Animal for Dog {
fn name(&self) -> String {
"小狗".to_string()
}
// 可選覆蓋:
fn intro(&self) -> String {
format!("{} 汪汪", self.name())
}
}
struct Cat;
impl Animal for Cat {
fn name(&self) -> String {
"小貓".to_string()
}
// 用默認 intro
}
fn main() {
println!("{}", Dog.intro()); // 小狗 汪汪
println!("{}", Cat.intro()); // 我是 小貓
}
// 默認方法可調用 trait 其他方法

trait 對象

dyn Trait 動態分派。異構集合。運行時多態。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
trait Draw {
fn draw(&self);
}
struct Circle;
struct Square;
impl Draw for Circle {
fn draw(&self) {
println!("畫圓");
}
}
impl Draw for Square {
fn draw(&self) {
println!("畫方");
}
}
// 動態分派:
fn draw_all(shapes: &[&dyn Draw]) {
for s in shapes {
s.draw();
}
}
// 裝箱 trait 對象:
fn make() -> Box<dyn Draw> {
Box::new(Circle)
}
fn main() {
let shapes: Vec<&dyn Draw> =
vec![&Circle, &Square];
draw_all(&shapes);
// 運行時決定調用哪個實現
// dyn 有運行時開銷
// 靜態分派用泛型更快
// 對象安全限制
}

泛型

泛型類型與函數。T 抽象。代碼複用。單態化零開銷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 泛型函數:
fn identity<T>(x: T) -> T {
x
}
// 泛型結構體:
struct Point<T> {
x: T,
y: T,
}
// 多個類型參數:
struct Pair<A, B> {
first: A,
second: B,
}
// 泛型方法:
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 1, y: 2 }; // 推斷 i32
println!("{}", p.x());
let ip = Point { x: 1.5, y: 2.5 };
println!("{}", ip.x());
// 單態化:
// 編譯期生成具體版本
// 無運行時開銷
// 與 dyn 對比更高效

泛型約束

T: Trait 約束。where 從句。約束 trait 組合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 基本約束:
fn print_it<T: std::fmt::Debug>(x: &T) {
println!("{:?}", x);
}
// where 從句:
fn complex<T, U>(a: &T, b: &U)
where
T: std::fmt::Display + Clone,
U: std::fmt::Debug,
{
println!("{}", a);
println!("{:?}", b);
}
// 返回泛型:
fn pair() -> impl std::fmt::Display {
42 // 不透明返回類型
}
// trait 約束泛型集合:
fn sortable<T: Ord>(list: &mut [T]) {
list.sort();
}
// 約束使泛型方法可用
// impl Trait 參數簡寫
// 多約束用 + 組合

impl 塊

impl 關聯方法與關聯函數。self、Self。構造函數約定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 關聯函數(無 self):
fn new(w: u32, h: u32) -> Rectangle {
Rectangle { width: w, height: h }
}
// 方法(&self):
fn area(&self) -> u32 {
self.width * self.height
}
// 可變方法(&mut self):
fn set_width(&mut self, w: u32) {
self.width = w;
}
// 消耗 self:
fn into_string(self) -> String {
format!("{}x{}", self.width, self.height)
}
}
fn main() {
let mut r = Rectangle::new(2, 3);
println!("{}", r.area());
r.set_width(5);
// Self 類型在 impl 內引用自身
// 多 impl 塊可分開定義
}

組合與多態

Rust 無繼承。用 trait + 組合複用。默認實現替代基類。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 組合替代繼承:
struct Engine {
power: u32,
}
impl Engine {
fn start(&self) {
println!("引擎啓動 {} 馬力", self.power);
}
}
struct Car {
engine: Engine, // 組合
model: String,
}
impl Car {
fn start(&self) {
self.engine.start(); // 委託
println!("{} 發車", self.model);
}
}
// trait 默認方法做基類效果:
trait Fly {
fn fly(&self) {
println!("飛行");
}
}
struct Bird;
impl Fly for Bird {} // 用默認
// 多態:
fn make_fly(f: &dyn Fly) {
f.fly();
}
fn main() {
let car = Car {
engine: Engine { power: 150 },
model: String::from("Guru"),
};
car.start();
}
// 組合更靈活、無菱形繼承問題

11.錯誤處理

Result、Option、? 運算符、panic 與自定義錯誤。

Result

Result<T, E> 返回成功值或錯誤。Ok/Err 變體。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use std::fs::File;
fn main() {
// Result 枚舉:
let result = File::open("nope.txt");
// match 處理:
match result {
Ok(file) => println!("打開成功"),
Err(e) => println!("打開失敗: {}", e),
}
// 泛型簽名:
// enum Result<T, E> {
// Ok(T),
// Err(E),
// }
// 手動構造:
let ok: Result<i32, String> = Ok(42);
let err: Result<i32, String> =
Err(String::from("失敗"));
// 方法:
let v = ok.unwrap_or(0);
let v2 = ok.unwrap_or_else(|_| -1);
}

unwrap 與 expect

unwrap 成功取值,失敗 panic。expect 帶自定義消息。調試用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
let ok: Result<i32, &str> = Ok(10);
// unwrap:
let v = ok.unwrap();
println!("{}", v);
// 失敗時 panic:
// let e: Result<i32, &str> = Err("bad");
// e.unwrap(); // panic!
// expect 帶消息:
// let e = Err("bad");
// e.expect("應該成功"); // panic 消息
// 適合場景:
// 原型/示例/測試
// 邏輯上不可能失敗
// 生產建議:
// 用 ? 傳播
// 或 match 處理
// unwrap_or 提供默認值

? 運算符

? 簡化錯誤傳播。Err 自動返回,Ok 解包繼續。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::fs::File;
use std::io::Read;
// 傳播錯誤:
fn read_file(path: &str) -> Result<String, std::io::Error> {
let mut f = File::open(path)?; // Err 直接返回
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
// 鏈式:
fn first_line(path: &str) -> Result<String, std::io::Error> {
let content = read_file(path)?;
Ok(content.lines().next().unwrap_or("").to_string())
}
// ? 自動轉換錯誤:
// 需要 From 實現
// fn f() -> Result<(), Box<dyn Error>> {
// File::open("x")?; // io::Error 自動裝箱
// Ok(())
// }
// 函數必須返回 Result/Option

實現 Error

標準 Error trait。Display + Debug 打印。錯誤鏈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::error::Error;
use std::fmt;
// 自定義錯誤類型:
#[derive(Debug)]
struct MyError {
msg: String,
}
// Display:
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "發生錯誤: {}", self.msg)
}
}
// 實現 Error:
impl Error for MyError {}
fn main() {
let e = MyError { msg: "xxx".to_string() };
println!("{}", e); // Display
println!("{:?}", e); // Debug
println!("{}", e.source()); // 無來源
}
// Error trait 要求 Display + Debug
// source() 返回底層錯誤
// 用於 ? 和錯誤鏈

自定義錯誤

設計自己的錯誤類型。枚舉變體攜帶上下文。錯誤分類。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#[derive(Debug)]
// 分類錯誤:
enum AppError {
NotFound(String),
InvalidInput { field: String, reason: String },
Io(std::io::Error),
}
// 手動實現 Display:
impl std::fmt::Display for AppError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
AppError::NotFound(msg) =>
write!(f, "未找到: {}", msg),
AppError::InvalidInput { field, reason } =>
write!(f, "{} 無效: {}", field, reason),
AppError::Io(e) => write!(f, "IO: {}", e),
}
}
}
impl std::error::Error for AppError {}
// 轉換:
impl From<std::io::Error> for AppError {
fn from(e: std::io::Error) -> AppError {
AppError::Io(e)
}
}
// 使用:
// fn f() -> Result<(), AppError> {
// std::fs::read_to_string("x")?; // 自動轉換
// Ok(())
// }

thiserror

thiserror 派生宏自動實現 Display/Error。減少樣板代碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Cargo.toml 添加:
// thiserror = "2"
use thiserror::Error;
// 派生宏:
#[derive(Error, Debug)]
// 錯誤枚舉:
#[derive(Error, Debug)]
enum DataError {
#[error("記錄不存在: {0}")]
NotFound(String),
#[error("校驗失敗: {field} {reason}")]
Validation {
field: String,
reason: String,
},
#[error(transparent)]
Io(#[from] std::io::Error),
}
// 自動獲得:
// Display 實現
// Error 實現
// From<std::io::Error> 轉換
fn main() {
let e = DataError::NotFound("id".into());
println!("{}", e);
}
// 大幅減少手寫樣板
// 生態標準做法

Option

Option<T> 處理可能缺失。Some/None。安全訪問。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
// Option 類型:
let some = Some(5);
let none: Option<i32> = None;
// match:
match some {
Some(v) => println!("值 {}", v),
None => println!("無值"),
}
// 常用方法:
let x = some.unwrap_or(0);
let y = some.unwrap_or_else(|| compute());
let z = none.unwrap_or(0); // 0
// 鏈式:
let doubled = some.map(|v| v * 2);
let filtered = some.filter(|v| v > 3);
let flat = Some(Some(1)).flatten();
// 組合:
let a = Some(1);
let b = Some(2);
let sum = a.zip(b).map(|(x, y)| x + y);
// 常見:
// Vec::get / HashMap::get
// 用 ? 傳播 None

panic

panic! 不可恢復錯誤。程序崩潰並展開棧。僅用於bug。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn main() {
// 觸發 panic:
// panic!("致命錯誤");
// 索引越界:
let v = vec![1, 2, 3];
// let x = v[10]; // panic!
// 空 slice 取首:
let empty: &[i32] = &[];
// let x = empty[0]; // panic!
// panic 行為:
// 打印錯誤信息
// 展開棧,運行 Drop
// 程序終止
// 自定義消息:
// panic!("索引 {} 越界", 10);
// 適用場景:
// 程序無法繼續的 bug
// 契約被破壞
// 可恢復用 Result
}

12.輸入輸出

標準輸入輸出、文件讀寫與文件系統。

讀取輸入

stdin 讀取一行。read_line 到 String。trim 去除換行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use std::io;
fn main() {
// 讀取一行:
let mut input = String::new();
io::stdin().read_line(&mut input)
.expect("讀取失敗");
// 去除換行符:
let trimmed = input.trim();
println!("你輸入了: {}", trimmed);
// 讀取數字:
let mut line = String::new();
io::stdin().read_line(&mut line).unwrap();
let n: i32 = line.trim().parse().unwrap();
println!("數字: {}", n);
// 循環讀取:
loop {
let mut s = String::new();
let bytes = io::stdin().read_line(&mut s).unwrap();
if bytes == 0 { break; } // EOF
print!("{}", s);
}
}

讀取文件

read_to_string 一次性讀入。Read trait 流式讀取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::fs;
fn main() {
// 一次性讀取為字符串:
let content = fs::read_to_string("data.txt")
.expect("讀取失敗");
println!("{}", content);
// 讀取為字節:
let bytes = fs::read("data.bin").unwrap();
// 流式讀取(大文件):
use std::io::{Read, BufReader};
let f = fs::File::open("big.log").unwrap();
let mut reader = BufReader::new(f);
let mut buf = String::new();
reader.read_to_string(&mut buf).unwrap();
// 逐行讀取:
use std::io::BufRead;
let f = fs::File::open("data.txt").unwrap();
for line in std::io::BufReader::new(f).lines() {
println!("{}", line.unwrap());
}
}

寫入文件

write/append 寫入。OpenOptions 控制模式。緩衝寫入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use std::fs;
fn main() {
// 覆蓋寫入:
fs::write("out.txt", "hello\n")
.expect("寫入失敗");
// 追加寫入:
use std::fs::OpenOptions;
use std::io::Write;
let mut f = OpenOptions::new()
.append(true)
.create(true)
.open("log.txt")
.unwrap();
f.write_all(b"more data\n").unwrap();
// 緩衝寫入:
let mut writer = std::io::BufWriter::new(
fs::File::create("buf.txt").unwrap());
writeln!(writer, "第 {} 行", 1).unwrap();
writer.flush().unwrap(); // 刷入磁盤
// 格式寫入:
// write!(f, "{}", value)
}

文件系統

創建目錄、刪除、重命名、判斷存在。路徑操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use std::fs;
fn main() {
// 創建目錄:
fs::create_dir("data").unwrap();
fs::create_dir_all("data/a/b").unwrap();
// 刪除:
fs::remove_file("tmp.txt").unwrap();
fs::remove_dir("data/a").unwrap();
fs::remove_dir_all("data").unwrap();
// 重命名:
fs::rename("a.txt", "b.txt").unwrap();
// 判斷存在:
if fs::metadata("b.txt").is_ok() {
println!("存在");
}
// 元數據:
let meta = fs::metadata("b.txt").unwrap();
println!("大小: {}", meta.len());
println!("目錄: {}", meta.is_dir());
// 複製:
fs::copy("b.txt", "c.txt").unwrap();
}

BufRead 逐行

BufRead trait 逐行讀取。lines()、read_line、split。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::io::BufRead;
use std::io::BufReader;
use std::fs::File;
fn main() {
let f = File::open("data.txt").unwrap();
let reader = BufReader::new(f);
// 逐行:
for line in reader.lines() {
let l = line.unwrap();
println!("{}", l);
}
// 帶行號:
let f = File::open("data.txt").unwrap();
for (i, line) in
BufReader::new(f).lines().enumerate() {
println!("{}: {}", i + 1, line.unwrap());
}
// 手動讀取一行:
// let mut buf = String::new();
// reader.read_line(&mut buf)
// 分割字節:
// for part in reader.split(b",") {}
// 從 stdin 也可以用
}

輸出與寫入

print!/println!/eprintln!。write!/writeln! 到任意寫入者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::io::{self, Write};
fn main() {
// 標準輸出:
println!("普通輸出");
print!("無換行");
// 標準錯誤:
eprintln!("錯誤信息");
// 寫入 Vec:
let mut buf = Vec::new();
writeln!(buf, "寫入 Vec {}", 1).unwrap();
println!("{:?}", buf);
// 寫入 String:
let mut s = String::new();
write!(s, "格式化 {}", 42).unwrap();
println!("{}", s);
// 寫入文件(見文件節)
// io::stdout() 也可用:
// let mut out = io::stdout();
// writeln!(out, "直接寫 stdout")
// Flush 手動刷新
}

標準流

stdin/stdout/stderr 標準流。交互與管道數據。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use std::io::{self, Read, Write};
fn main() {
// 管道讀取全部:
// echo data | program
let mut buf = String::new();
io::stdin().read_to_string(&mut buf).unwrap();
// 逐行處理管道:
// for line in io::stdin().lines() {
// println!("{} ", line.unwrap());
// }
// 輸出到 stdout:
println!("處理完成");
// 錯誤到 stderr:
eprintln!("警告: 非致命");
// 寫入 stdout:
// io::stdout().write_all(b"raw\n")
// 交互提示:
// print!("輸入名字: ");
// io::stdout().flush().unwrap();
}

字節讀取

read 讀字節到緩衝。流式處理二進制。write_all 寫全部。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::fs::File;
use std::io::{self, Read, Write};
fn main() {
// 讀取到緩衝:
let mut f = File::open("data.bin").unwrap();
let mut buf = [0u8; 1024];
let n = f.read(&mut buf).unwrap();
println!("讀取 {} 字節", n);
// 循環讀取直到 EOF:
let mut f = File::open("data.bin").unwrap();
let mut total = 0;
let mut buf = [0u8; 256];
loop {
let n = f.read(&mut buf).unwrap();
if n == 0 { break; } // EOF
total += n;
// 處理 buf[..n]
}
// 寫入全部:
let mut out = File::create("out.bin").unwrap();
out.write_all(&[1, 2, 3, 4]).unwrap();
// 讀取全部字節:
let all = std::fs::read("data.bin").unwrap();
println!("總 {} 字節", all.len());
}

13.常見誤區

Rust 新手最容易踩的坑與正確寫法。

移動後使用

值被移動後不能使用。借用或 clone 保留所有權。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// BAD:移動後訪問
fn main() {
let s = String::from("hi");
let t = s; // 移動
// println!("{}", s); // 錯誤:已移動
println!("{}", t);
}
// GOOD:借用
fn main() {
let s = String::from("hi");
let t = &s; // 借用
println!("{} {}", s, t);
}
// GOOD:顯式克隆
let s = String::from("hi");
let t = s.clone();
// 場景:
// 函數傳值轉移所有權
// 需要兩處使用
// 借用更高效,clone 更直接

借用衝突

不可變借用與可變借用不能並存。作用域縮短借用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// BAD:同時借用
fn main() {
let mut v = vec![1, 2, 3];
// let r1 = &v; // 不可變
// v.push(4); // 可變,衝突
// println!("{:?}", r1);
}
// GOOD:先結束不可變借用
fn main() {
let mut v = vec![1, 2, 3];
let r1 = &v[0];
println!("{}", r1); // 借用結束
v.push(4); // 現在允許
}
// GOOD:分開作用域
{
let r = &v;
println!("{:?}", r);
} // 借用結束
v.push(4);
// 規則:
// 讀讀可以,讀寫不行
// 編譯器保證無數據競爭

懸垂引用

返回局部變量的引用會懸垂。返回擁有所有權或生命週期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// BAD:返回局部引用
// fn bad() -> &String {
// let s = String::from("x");
// &s // 錯誤:s 被釋放
// }
// GOOD:返回所有權
fn good() -> String {
String::from("x")
}
// GOOD:引用輸入
fn first_char(s: &str) -> &str {
&s[..1]
}
// GOOD:生命週期參數
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// 規則:
// 返回的引用必須有效
// 指向堆或參數的生命週期
// 編譯器(借用檢查器)攔截

String 拼接

+ 拼接會移動左側。重複拼接性能差。用 format! 或 push_str。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// BAD:重複 + 拼接
let mut s = String::new();
// for _ in 0..10 {
// s = s + "x"; // 每次重新分配
// }
// GOOD:push_str 追加
let mut s = String::new();
for _ in 0..10 {
s.push_str("x");
}
// GOOD:format! 一次性
let a = "hello";
let b = "world";
let s = format!("{}, {}", a, b);
// + 語法:
let s1 = String::from("a");
let s2 = String::from("b");
let s3 = s1 + &s2; // s1 被移動
// 原因:
// + 需要 String 左操作數
// 大量拼接擴容頻繁
// format!/join 更優

索引類型

索引與長度是 usize,不能直接是 i32。集合索引類型不匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// BAD:i32 索引
fn main() {
let v = vec![1, 2, 3];
let i: i32 = 1;
// let x = v[i]; // 錯誤:usize 需要
// let x = v[i as usize]; // 轉換
}
// GOOD:直接 usize
fn main() {
let v = vec![1, 2, 3];
let i: usize = 1;
let x = v[i]; // 正確
println!("{}", x);
}
// 場景:
// for (i, x) in v.iter().enumerate()
// 返回的是 usize
// len() 也是 usize
// 計算距離類型不匹配時:
// 用 as usize 轉換
// 或調整變量類型

match 分支類型

match 所有分支返回類型必須一致。缺分支需 _ 通配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// BAD:分支類型不一致
// let x = match 5 {
// 1 => "one",
// _ => 42, // 錯誤:str vs i32
// };
// GOOD:統一返回類型
let x = match 5 {
1 => "one",
_ => "other", // 都是 &str
};
// BAD:不窮盡
// enum Color { R, G, B }
// let c = Color::R;
// match c {
// Color::R => {}
// Color::G => {}
// // 缺 B,編譯錯誤
// }
// GOOD:加通配
// match c {
// Color::R => {}
// _ => {}
// }
// 規則:
// 窮盡性編譯期檢查
// 類型推斷按首個分支

閉包捕獲

閉包自動按需借用或移動。同時借用衝突。move 顯式轉移。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// BAD:閉包借用與修改衝突
fn main() {
let mut count = 0;
// let print = || println!("{}", count);
// count += 1; // 衝突:已被借用
// print();
}
// GOOD:先結束閉包
fn main() {
let mut count = 0;
{
let print = || println!("{}", count);
print();
} // 借用結束
count += 1;
}
// GOOD:move 閉包
let data = vec![1, 2];
let handler = move || {
println!("{:?}", data); // 擁有所有權
};
// 規則:
// Fn / FnMut / FnOnce
// 按捕獲方式自動選擇
// 線程傳閉包需 move
// 衝突時縮小借用範圍

整數溢出

調試構建溢出 panic。發佈構建迴繞。顯式處理或選類型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// BAD:調試構建溢出 panic
// fn main() {
// let x: u8 = 255;
// let y = x + 1; // 調試 panic!
// }
// GOOD:顯式檢查
fn main() {
let x: u8 = 255;
match x.checked_add(1) {
Some(v) => println!("{}", v),
None => println!("溢出"),
}
// 其他方法:
// x.checked_add / checked_mul
// x.wrapping_add // 迴繞
// x.saturating_add // 飽和到上限
// x.overflowing_add // 返回進位
// 發佈構建默認迴繞
// 大計數用 u64/usize
// 明確語義選對方法

14.併發

線程、channel、共享狀態與 Send/Sync。

線程

thread::spawn 啓動新線程。join 等待完成。返回值可傳遞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::thread;
use std::time::Duration;
fn main() {
// 啓動線程:
let handle = thread::spawn(|| {
for i in 1..3 {
println!("子線程 {}", i);
thread::sleep(Duration::from_millis(10));
}
});
// 主線程繼續:
for i in 1..3 {
println!("主線程 {}", i);
}
// 等待子線程結束:
handle.join().unwrap();
// join 可獲取返回值:
let h = thread::spawn(|| 42);
let result = h.join().unwrap();
println!("{}", result); // 42
}

move 閉包

move 閉包把捕獲值移入線程。避免借用懸垂。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::thread;
fn main() {
let data = String::from("hello");
// 線程借用 data:
let h1 = thread::spawn(move || {
println!("{} 世界", data);
});
// data 已移動,主線程不能再用
// 需要多線程共享:
// 用 Arc(見共享節)
// 循環中 move:
let mut handles = vec![];
for i in 0..3 {
handles.push(thread::spawn(move || {
println!("線程 {}", i);
}));
}
for h in handles {
h.join().unwrap();
}
// 無 move 閉包:
// 捕獲引用,可能借用主線程
// 線程可能比借用活得更久
// move 明確轉移所有權

channel

mpsc 多生產者單消費者。send/recv 傳輸數據。類型安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use std::sync::mpsc;
use std::thread;
fn main() {
// 創建通道:
let (tx, rx) = mpsc::channel();
// 發送端移動到子線程:
thread::spawn(move || {
tx.send(String::from("hello")).unwrap();
});
// 接收:
let received = rx.recv().unwrap();
println!("收到: {}", received);
// recv 阻塞等待
// 無值發送者全 Drop 時 recv 報錯
// try_recv 非阻塞:
// match rx.try_recv() {
// Ok(v) => ...,
// Err(_) => println!("暫無數據"),
// }
// 迭代接收:
// for v in rx {
// println!("{}", v);
// }

多生產者

Sender 克隆實現多生產者。共享一個接收端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
// 克隆發送端:
let tx1 = tx.clone();
let tx2 = tx.clone();
// 三個生產者:
thread::spawn(move || {
tx.send(1).unwrap();
});
thread::spawn(move || {
tx1.send(2).unwrap();
});
thread::spawn(move || {
tx2.send(3).unwrap();
});
// 主線程消費:
for _ in 0..3 {
let v = rx.recv().unwrap();
println!("收到 {}", v);
}
// 接收順序不保證
// 單消費者:
// mpsc 只允許一個 rx
// 多消費者需拆分或互斥
// 通道是消息傳遞首選
// 避免共享可變狀態

共享可變

Arc<Mutex<T>> 多線程共享可變數據。鎖保護讀寫。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// 共享計數器:
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1; // 臨界區
}));
}
for h in handles {
h.join().unwrap();
}
println!("結果: {}", *counter.lock().unwrap());
// Mutex 保證互斥
// Arc 保證引用計數線程安全
// 鎖 Guard 離開作用域釋放
// 持鎖時間儘量短
// 避免鎖中調用會再取鎖
// 死鎖風險注意順序

scoped 線程

thread::scope 借用非 'static 數據。無需 move 所有權。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::thread;
fn main() {
let mut data = vec![1, 2, 3];
// 借用作用域內數據:
thread::scope(|s| {
// 不可變借用:
s.spawn(|| {
println!("{:?}", data);
});
// 可變借用(分線程):
s.spawn(|| {
data.push(4); // 僅此線程用
});
}); // 作用域結束,全部線程完成
println!("{:?}", data);
// 與 thread::spawn 區別:
// spawn 要求 'static 所有權
// scope 可借用局部變量
// scope 結束時自動 join
// 適用:並行處理局部數據
}

rayon 並行

rayon 並行迭代器。par_iter 替代 iter。數據並行簡單化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Cargo.toml:
// rayon = "1"
use rayon::prelude::*;
fn main() {
let v: Vec<i32> = (0..1000).collect();
// 並行求和:
let sum: i32 = v.par_iter().sum();
println!("{}", sum);
// 並行映射:
let doubled: Vec<i32> =
v.par_iter().map(|x| x * 2).collect();
// 並行過濾:
let evens: Vec<&i32> =
v.par_iter().filter(|x| **x % 2 == 0).collect();
// 並行排序:
let mut sorted = vec![3, 1, 2];
sorted.par_sort();
// 數據必須 Send
// 自動分塊並行
// 大集合收益明顯
// 小集合開銷更大
// 結果與串行一致

Send 與 Sync

Send 可跨線程移動。Sync 可被引用共享。安全併發保障。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Send:所有權可跨線程
// 絕大多數類型是 Send
// 例外:Rc<T> 不是 Send
// Sync:可被 &T 共享
// 例外:RefCell 不是 Sync
use std::rc::Rc;
use std::thread;
fn main() {
// Rc 不能進線程:
// let rc = Rc::new(5);
// thread::spawn(move || println!("{}", rc));
// 錯誤:Rc 不是 Send
// 用 Arc 替代:
let arc = std::sync::Arc::new(5);
let h = thread::spawn(move || println!("{}", arc));
h.join().unwrap();
// 規則:
// Send 類型可跨線程
// Sync 類型可安全共享引用
// 編譯器檢查跨線程使用
// 自定義類型自動派生
// unsafe impl 需謹慎

15.網絡

TCP、UDP、HTTP 與第三方網絡庫。

TCP 連接

TcpStream 建立連接。read/write 收發數據。流式協議。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use std::net::TcpStream;
use std::io::{Read, Write};
fn main() {
// 建立連接:
let mut stream = TcpStream::connect(
"example.com:80").unwrap();
// 發送 HTTP 請求:
stream.write_all(
b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
.unwrap();
// 讀取響應:
let mut buf = [0u8; 4096];
let n = stream.read(&mut buf).unwrap();
println!("{}", String::from_utf8_lossy(&buf[..n]));
// 連接錯誤處理:
// TcpStream::connect 返回 Result
// 設置超時:
// stream.set_read_timeout(Some(Duration::from_secs(5)))
// 雙向流式通信
}

TCP 監聽

TcpListener 綁定監聽端口。accept 接受連接。多連接線程處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::net::{TcpListener, TcpStream};
use std::io::{Read, Write};
use std::thread;
fn main() {
// 綁定端口:
let listener = TcpListener::bind("127.0.0.1:8080")
.unwrap();
println!("監聽 8080");
// 接受連接:
for stream in listener.incoming() {
match stream {
Ok(stream) => {
// 每連接一線程:
thread::spawn(|| handle(stream));
}
Err(e) => eprintln!("連接錯誤: {}", e),
}
}
}
fn handle(mut stream: TcpStream) {
let mut buf = [0u8; 1024];
let n = stream.read(&mut buf).unwrap();
println!("收到 {} 字節", n);
stream.write_all(&buf[..n]).unwrap();
}

HTTP 請求

reqwest 發起 HTTP 請求。JSON 請求與響應。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Cargo.toml:
// reqwest = { version = "0.12", features = ["json"] }
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// GET 請求:
let resp = reqwest::Client::new()
.get("https://api.example.com/users")
.timeout(Duration::from_secs(10))
.send()
.await?;
// 狀態碼:
println!("狀態: {}", resp.status());
// 讀取文本:
let body = resp.text().await?;
println!("{}", &body[..200]);
Ok(())
}
// 需 tokio 運行時:
// tokio = { version = "1", features = ["full"] }
// JSON:
// .json::<T>() 反序列化
// serde 配合

HTTP 服務

axum 構建 HTTP 服務。路由與處理器。異步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Cargo.toml:
// axum = "0.7"
// tokio = { version = "1", features = ["full"] }
// serde_json = "1"
use axum::{Router, routing::get};
use axum::response::Json;
use serde_json::{json, Value};
#[tokio::main]
async fn main() {
// 路由:
let app = Router::new()
.route("/", get(root))
.route("/api/ping", get(ping));
// 啓動:
let listener = tokio::net::TcpListener::bind(
"127.0.0.1:3000").await.unwrap();
println!("服務運行在 3000");
axum::serve(listener, app).await.unwrap();
}
async fn root() -> &'static str {
"Hello, GuruToolkit!"
}
async fn ping() -> Json<Value> {
Json(json!({ "ok": true }))
}
// 路由參數:
// .route("/user/{id}", get(user_by_id))

URL 處理

url crate 解析與構建 URL。查詢參數。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Cargo.toml:
// url = "2"
use url::Url;
fn main() {
// 解析:
let url = Url::parse(
"https://[email protected]/path?q=rust#sec")
.unwrap();
// 訪問字段:
println!("{}", url.scheme()); // https
println!("{}", url.host_str().unwrap());
println!("{}", url.path());
println!("{}", url.fragment().unwrap());
// 查詢參數:
let pairs: Vec<(String, String)> =
url.query_pairs().into_owned().collect();
for (k, v) in pairs {
println!("{}={}", k, v);
}
// 構建 URL:
let mut u = Url::parse("https://api.com").unwrap();
u.set_path("/v1/users");
u.query_pairs_mut()
.append_pair("page", "2");
println!("{}", u);
}

IP 解析

std::net::IpAddr 解析 IP。判斷 IPv4/IPv6。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
fn main() {
// 解析 IP:
let ip: IpAddr = "192.168.1.1".parse().unwrap();
println!("{}", ip);
// 判斷版本:
match ip {
IpAddr::V4(v) => println!("IPv4: {}", v),
IpAddr::V6(v) => println!("IPv6: {}", v),
}
// 構造:
let v4 = Ipv4Addr::new(127, 0, 0, 1);
let v6: Ipv6Addr = "::1".parse().unwrap();
// 屬性:
println!("迴環: {}", v4.is_loopback());
println!("私有: {}", v4.is_private());
// 端口組合:
let addr = "127.0.0.1:8080".parse::<std::net::SocketAddr>().unwrap();
println!("{}", addr.port());
// 域名解析:
// use std::net::ToSocketAddrs;
// let mut it = "example.com:80".to_socket_addrs().unwrap();
}

UDP

UdpSocket 無連接數據報。send_to/recv_from。無需握手。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use std::net::UdpSocket;
fn main() {
// 綁定本地:
let socket = UdpSocket::bind("127.0.0.1:0")
.unwrap();
// 發送數據報:
socket.send_to(b"hello", "127.0.0.1:9999")
.unwrap();
// 接收:
let mut buf = [0u8; 1024];
let (n, src) = socket.recv_from(&mut buf).unwrap();
println!("來自 {}: {}", src,
String::from_utf8_lossy(&buf[..n]));
// 服務端:
let server = UdpSocket::bind("127.0.0.1:9999")
.unwrap();
// 特點:
// 無連接、無握手
// 可能丟包亂序
// 效率高於 TCP
// 適合:DNS、視頻、遊戲
// 可靠傳輸自行實現
}

DNS 解析

ToSocketAddrs 解析域名。查地址列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use std::net::ToSocketAddrs;
fn main() {
// 域名解析:
let addrs = "example.com:80"
.to_socket_addrs().unwrap();
for addr in addrs {
println!("{}", addr);
}
// 端口默認:
let addrs = "example.com"
.to_socket_addrs().unwrap();
// 解析可能返回多個地址
// 依次嘗試連接
// 錯誤處理:
match "bad-domain.invalid:80".to_socket_addrs() {
Ok(it) => println!("解析成功"),
Err(e) => println!("解析失敗: {}", e),
}
// 異步 DNS:
// 第三方庫 tokio::net
// 生產用 resolv-conf
// 測試用本地 hosts

16.時間與日期

SystemTime、Instant、Duration 與 chrono。

SystemTime

SystemTime 系統時鐘。獲取當前時間。與 UNIX 紀元比較。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::time::SystemTime;
fn main() {
// 當前時間:
let now = SystemTime::now();
// UNIX 紀元:
let epoch = SystemTime::UNIX_EPOCH;
// 距紀元:
match now.duration_since(epoch) {
Ok(d) => println!("距紀元 {:?}", d),
Err(e) => println!("系統時間早於紀元: {:?}", e),
}
// 比較:
let later = SystemTime::now();
let elapsed = later.duration_since(now).unwrap();
println!("經過 {:?}", elapsed);
// 限制:
// 不精確(系統時鐘可能調)
// 測耗時用 Instant
// 日期處理用 chrono
// 時間戳:
let secs = now.duration_since(epoch)
.unwrap().as_secs();
println!("{} 秒", secs);
}

Instant 計時

Instant 單調時鐘測量耗時。不受系統時間調整影響。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::time::Instant;
fn main() {
// 開始計時:
let start = Instant::now();
// 執行耗時操作:
std::thread::sleep(std::time::Duration::from_millis(50));
// 計算耗時:
let elapsed = start.elapsed();
println!("耗時: {:?}", elapsed);
// 不同單位:
println!("{} 微秒", elapsed.as_micros());
println!("{} 毫秒", elapsed.as_millis());
// 多個起點比較:
let a = Instant::now();
let b = Instant::now();
let diff = b.duration_since(a);
// 使用場景:
// 性能基準、超時控制
// 單調遞增、不受時鐘影響
// 不能跨進程比較
}

Duration

Duration 時間段。秒/納秒精度。算術與轉換。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use std::time::Duration;
fn main() {
// 創建:
let sec = Duration::from_secs(5);
let ms = Duration::from_millis(1500);
let us = Duration::from_micros(100);
let ns = Duration::from_nanos(1);
// 組合:
let d = Duration::new(2, 500_000_000); // 2.5s
// 算術:
let sum = sec + ms;
let mul = sec * 2;
// 比較:
let a = Duration::from_secs(1);
let b = Duration::from_millis(500);
println!("{} > {}", a > b, a > b);
// 轉換:
println!("{} 秒", sec.as_secs());
println!("{} 毫秒", ms.as_millis());
// 用途:
// sleep、超時、等待
// 網絡超時配置
}

chrono 當前時間

chrono crate 處理日期時間。Utc/Local 時區。人類可讀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Cargo.toml:
// chrono = "0.4"
use chrono::{Utc, Local, NaiveDate};
fn main() {
// UTC 時間:
let utc = Utc::now();
println!("{}", utc);
// 本地時間:
let local = Local::now();
println!("{}", local);
// 訪問字段:
println!("年: {}", utc.year());
println!("月: {}", utc.month());
println!("日: {}", utc.day());
println!("時: {}", utc.hour());
println!("星期: {}", utc.weekday());
// 時間戳:
println!("{}", utc.timestamp());
// 日期構造:
let d = NaiveDate::from_ymd_opt(2026, 8, 2)
.unwrap();
println!("{}", d);
// 時區標記:
// +00:00 UTC / +08:00 中國
}

格式化輸出

format 格式化日期。strftime 風格佔位符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// chrono = "0.4"
use chrono::Utc;
fn main() {
let now = Utc::now();
// 格式化:
println!("{}", now.format("%Y-%m-%d")); // 2026-08-02
println!("{}", now.format("%H:%M:%S")); // 14:30:22
println!("{}", now.format("%Y-%m-%d %H:%M"));
// 常用佔位符:
// %Y 年 %m 月 %d 日
// %H 時 %M 分 %S 秒
// %A 星期全名 %a 縮寫
// %B 月份全名
// %j 年中第幾天
// 自定義:
println!("{}", now.format("%Y/%m/%d %H:%M:%S %z"));
// 默認 Display:
println!("{}", now);
// 返回 String:
let s = now.format("%F").to_string();
println!("{}", s); // ISO 日期
}

解析字符串

DateTime::parse_from_str 解析。ISO 8601 解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// chrono = "0.4"
use chrono::{DateTime, NaiveDateTime, Utc};
fn main() {
// 解析固定格式:
let dt = DateTime::parse_from_str(
"2026-08-02 10:30:00 +0800",
"%Y-%m-%d %H:%M:%S %z").unwrap();
println!("{}", dt);
// ISO 8601:
let iso = DateTime::parse_from_rfc3339(
"2026-08-02T10:30:00+08:00").unwrap();
println!("{}", iso);
// 無時區解析:
let naive = NaiveDateTime::parse_from_str(
"2026-08-02 10:30:00",
"%Y-%m-%d %H:%M:%S").unwrap();
println!("{}", naive);
// 轉 UTC:
let utc: DateTime<Utc> = iso.with_timezone(&Utc);
println!("{}", utc);
// 解析失敗返回 Err
}

時間運算

加減時間、計算間隔、時間範圍判斷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// chrono = "0.4"
use chrono::{Duration, Utc, Local};
fn main() {
let now = Utc::now();
// 加減:
let tomorrow = now + Duration::days(1);
let next_hour = now + Duration::hours(1);
let last_week = now - Duration::weeks(1);
println!("{}", tomorrow.format("%F"));
// 間隔:
let later = now + Duration::minutes(90);
let diff = later - now;
println!("間隔 {} 分鐘", diff.num_minutes());
// 日期範圍判斷:
let start = now - Duration::hours(1);
let end = now + Duration::hours(1);
let now_local = Local::now();
let n = now_local.with_timezone(&Utc);
if n >= start && n <= end {
println!("在範圍內");
}
// 時區:
// FixedOffset::east_opt(8 * 3600)
// 中國時區 +08:00
}

時區處理

FixedOffset 固定時區。本地時區偏移。跨時區轉換。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// chrono = "0.4"
use chrono::{Utc, Local, FixedOffset, TimeZone};
fn main() {
// 固定時區(中國 +08:00):
let cst = FixedOffset::east_opt(8 * 3600).unwrap();
let now = Utc::now();
let beijing = now.with_timezone(&cst);
println!("北京: {}", beijing);
// 本地時區:
let local = Local::now();
println!("本地: {}", local);
// 轉換回 UTC:
let utc = beijing.with_timezone(&Utc);
println!("UTC: {}", utc);
// 任意時區:
let tokyo = FixedOffset::east_opt(9 * 3600).unwrap();
let tokyo_now = now.with_timezone(&tokyo);
println!("東京: {}", tokyo_now);
// 偏移量:
println!("偏移: {}", cst); // +08:00
// 存數據建議存 UTC
// 顯示時再轉時區
}

17.進程與環境

命令行參數、環境變量、子進程與路徑。

參數與環境

std::env 讀取參數與環境變量。args 與 var。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::env;
fn main() {
// 命令行參數:
let args: Vec<String> = env::args().collect();
println!("{:?}", args);
// 第一個是程序路徑
// 環境變量:
match env::var("PATH") {
Ok(v) => println!("PATH: {}", v),
Err(e) => println!("未設置: {:?}", e),
}
// 帶默認值:
let port = env::var("PORT")
.unwrap_or_else(|_| "8080".to_string());
// 判斷存在:
// env::var_os("KEY").is_some()
// 設置環境變量:
// env::set_var("KEY", "value")
// 遍歷全部:
// for (k, v) in env::vars() {}
}

clap 參數解析

clap 解析命令行參數。子命令、選項、幫助。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Cargo.toml:
// clap = { version = "4", features = ["derive"] }
use clap::Parser;
// 定義參數:
#[derive(Parser, Debug)]
#[command(name = "app", version, about)]
struct Args {
/// 用户名
#[arg(short, long)]
name: String,
/// 次數
#[arg(short, long, default_value_t = 1)]
count: u32,
/// 詳細輸出
#[arg(short, long)]
verbose: bool,
}
fn main() {
let args = Args::parse();
println!("{:?}", args);
for _ in 0..args.count {
println!("你好 {}", args.name);
}
}
// 運行:
// app --name Nick --count 3 -v
// 自動生成幫助:--help
// 參數校驗自動完成

std::process

退出碼、Command 子進程、當前進程信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use std::process;
fn main() {
// 退出碼:
// process::exit(0) 成功
// process::exit(1) 失敗
// 當前進程 ID:
println!("PID: {}", process::id());
// 運行子進程:
let out = process::Command::new("ls")
.arg("-l")
.output()
.expect("運行失敗");
println!("狀態: {}", out.status);
println!("輸出: {}",
String::from_utf8_lossy(&out.stdout));
// 檢查是否成功:
if out.status.success() {
println!("命令成功");
}
// 等待完成:
// process::Command::new("x").status()
}

Command 子進程

Command 控制子進程。參數、環境、管道、輸出捕獲。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::process::Command;
use std::io::Write;
fn main() {
// 帶參數運行:
let mut cmd = Command::new("echo");
cmd.arg("hello").arg("world");
let output = cmd.output().unwrap();
println!("{}",
String::from_utf8_lossy(&output.stdout));
// 管道輸入:
let mut child = Command::new("cat")
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn().unwrap();
child.stdin.as_mut().unwrap()
.write_all(b"data").unwrap();
// 設置環境變量:
let out = Command::new("sh")
.arg("-c")
.arg("echo $MYVAR")
.env("MYVAR", "hello")
.output().unwrap();
println!("{}",
String::from_utf8_lossy(&out.stdout));
}

PathBuf

Path/PathBuf 路徑操作。拼接、組件、規範化。跨平台。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::path::{Path, PathBuf};
fn main() {
// 創建:
let mut p = PathBuf::from("/tmp");
p.push("data");
p.push("file.txt");
println!("{}", p.display());
// 組件:
println!("父: {:?}", p.parent());
println!("名: {:?}", p.file_name());
println!("擴展名: {:?}", p.extension());
// 判斷:
println!("絕對: {}", p.is_absolute());
println!("存在: {}", p.exists());
// 規範化:
let messy = Path::new("./a/../b");
let clean = messy.canonicalize();
// 拼接字符串:
let joined = Path::new("/data")
.join(format!("{}.log", 2026));
println!("{}", joined.display());
// 與 &str 互轉:
let s = p.to_string_lossy();
}

工作目錄

當前目錄、切換目錄、用户目錄。路徑相關操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use std::env;
fn main() {
// 當前目錄:
let cwd = env::current_dir().unwrap();
println!("{}", cwd.display());
// 切換目錄:
// env::set_current_dir("/tmp").unwrap();
// 用户主目錄:
let home = env::home_dir();
match home {
Some(h) => println!("主目錄: {}", h.display()),
None => println!("無主目錄"),
}
// 臨時目錄:
let tmp = env::temp_dir();
println!("臨時: {}", tmp.display());
// 可執行路徑:
let exe = env::current_exe().unwrap();
println!("程序: {}", exe.display());
// 相對路徑組合:
let config = env::current_dir().unwrap()
.join("config")
.join("app.toml");
}

目錄遍歷

read_dir 遍歷目錄。遞歸收集文件。過濾類型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::fs;
fn main() {
// 讀取目錄:
let entries = fs::read_dir(".").unwrap();
for entry in entries {
let e = entry.unwrap();
let path = e.path();
// 類型判斷:
if path.is_dir() {
println!("[目錄] {}", path.display());
} else {
println!("[文件] {}", path.display());
}
}
// 收集文件名:
let files: Vec<String> = fs::read_dir("src")
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.path().is_file())
.map(|e| e.file_name().to_string_lossy()
.into_owned())
.collect();
println!("{:?}", files);
// 遞歸需自己實現或用 walkdir
}

信號處理

ctrlc crate 捕獲 Ctrl+C。優雅退出。清理資源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Cargo.toml:
// ctrlc = "3"
use std::sync::atomic::{
AtomicBool, Ordering
};
use std::sync::Arc;
fn main() {
// 捕獲 Ctrl+C:
let running = Arc::new(AtomicBool::new(true));
let r = Arc::clone(&running);
ctrlc::set_handler(move || {
println!("\n收到中斷,退出中...");
r.store(false, Ordering::SeqCst);
}).expect("設置信號處理失敗");
// 主循環:
while running.load(Ordering::SeqCst) {
// 處理任務...
std::thread::sleep(
std::time::Duration::from_millis(100));
}
println!("清理完成,再見");
}
// 用途:
// 保存狀態、關閉連接
// 優雅關閉服務
// 標準庫:
// 信號原生處理需 libc/unsafe

18.正則表達式

regex crate 匹配、捕獲、替換與分割。

編譯正則

Regex::new 編譯。r#... 原始字符串。編譯昂貴需複用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Cargo.toml:
// regex = "1"
use regex::Regex;
fn main() {
// 編譯:
let re = Regex::new(r"^[a-z]+$").unwrap();
// 原始字符串 r"..." 避免轉義
// 返回 Result,非法模式報錯
// 編譯較慢,複用:
// 用 lazy_static/OnceLock 緩存
// 或線程局部
// 常用模式:
let email = Regex::new(
r"^[\w.+-]+@[\w-]+\.[\w.]+$").unwrap();
println!("{}", email.is_match("[email protected]"));
// 錯誤:
match Regex::new(r"[") {
Ok(_) => {}
Err(e) => println!("正則錯誤: {}", e),
}
}

匹配判斷

is_match 判斷是否匹配。部分匹配。整串匹配需錨點。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use regex::Regex;
fn main() {
let re = Regex::new(r"\d+").unwrap();
// 是否包含匹配:
println!("{}", re.is_match("abc123")); // true
// 部分匹配默認:
// 只判斷是否出現
// 整串匹配:
let full = Regex::new(r"^\d+$").unwrap();
println!("{}", full.is_match("123")); // true
println!("{}", full.is_match("123a")); // false
// find 獲取位置:
let m = re.find("abc123").unwrap();
println!("位置 {}..{}", m.start(), m.end());
// 大小寫:
let case = Regex::new(r"(?i)rust").unwrap();
println!("{}", case.is_match("RUST"));
}

捕獲分組

捕獲組提取子串。captures 返回分組。命名組。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use regex::Regex;
fn main() {
// 捕獲組:
let re = Regex::new(r"(\d{4})-(\d{2})-(\d{2})")
.unwrap();
let caps = re.captures("日期 2026-08-02").unwrap();
// caps[0] 整個匹配:
println!("{}", &caps[0]); // 2026-08-02
// caps[1..] 各分組:
println!("年: {}", &caps[1]);
println!("月: {}", &caps[2]);
println!("日: {}", &caps[3]);
// 命名組:
let named = Regex::new(r"(?P<year>\d{4})")
.unwrap();
let caps = named.captures("2026").unwrap();
println!("年: {}", caps.name("year").unwrap().as_str());
// 匹配不到返回 None
// 多組迭代:
// captures_iter 全部匹配

全部匹配

find_iter 遍歷所有匹配。captures_iter 全部帶分組。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use regex::Regex;
fn main() {
let re = Regex::new(r"\d+").unwrap();
let text = "a1 b22 c333";
// 所有匹配:
let nums: Vec<&str> =
re.find_iter(text).map(|m| m.as_str()).collect();
println!("{:?}", nums); // ["1", "22", "333"]
// 迭代位置:
for m in re.find_iter(text) {
println!("{} 在 {}..{}", m.as_str(), m.start(), m.end());
}
// 分組全部:
let pair = Regex::new(r"(\w+)=(\w+)").unwrap();
for caps in pair.captures_iter("a=1 b=2") {
println!("{} = {}", &caps[1], &caps[2]);
}
// 無匹配返回空迭代器
}

替換

replace 替換匹配。$1 引用分組。replace_all 全部替換。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use regex::Regex;
fn main() {
let re = Regex::new(r"\d+").unwrap();
// 替換所有:
let out = re.replace_all("a1 b2", "#");
println!("{}", out); // a# b#
// 只替換第一個:
let first = re.replace("a1 b2", "#");
println!("{}", first); // a# b2
// 分組引用:
let name = Regex::new(r"(\w+),\s*(\w+)").unwrap();
let out = name.replace_all("Doe, John", "$2 $1");
println!("{}", out); // John Doe
// 回調替換:
let out = re.replace_all("a1 b2", |caps: &regex::Captures| {
let n: i32 = caps[0].parse().unwrap();
format!("{}", n * 2)
});
println!("{}", out); // a2 b4
}

分割

split 按正則分割字符串。splitn 限制數量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use regex::Regex;
fn main() {
// 按分隔符分割:
let re = Regex::new(r"[,;]+").unwrap();
let parts: Vec<&str> =
re.split("a,b;c,,d").collect();
println!("{:?}", parts); // ["a", "b", "c", "d"]
// 按空白分割:
let ws = Regex::new(r"\s+").unwrap();
let words: Vec<&str> =
ws.split("hello world").collect();
println!("{:?}", words);
// 限制數量:
let parts2: Vec<&str> = re.splitn("a,b,c", 2).collect();
println!("{:?}", parts2); // ["a", "b,c"]
// 保留匹配分隔符:
// let parts: Vec<&str> =
// re.split_inclusive("a,b", 0).collect();
// 簡單分割用 str::split 更快
// 複雜模式才用 regex

常見模式

常用正則寫法。郵箱、URL、手機號、IP。字符類。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use regex::Regex;
fn main() {
// 郵箱:
let email = Regex::new(
r"^[\w.+-]+@[\w-]+\.[\w.]+$").unwrap();
println!("{}", email.is_match("[email protected]"));
// URL:
let url = Regex::new(r"^https?://[\w.-]+(/\S*)?$").unwrap();
println!("{}", url.is_match("https://example.com"));
// 手機號(中國):
let phone = Regex::new(r"^1[3-9]\d{9}$").unwrap();
println!("{}", phone.is_match("13800138000"));
// 字符類:
// \d 數字 \w 單詞 \s 空白
// \D \W \S 反義
// + 一次以上 * 零次以上
// {2,4} 次數 {2,} 至少兩次
// ^ 開頭 $ 結尾
// [a-z] [^0-9] 字符範圍
// 貪婪 ? 變非貪婪:\d+?
// 分組 ( ) 或 |
}

標誌與大小寫

(?i) 忽略大小寫等內聯標誌。RegexBuilder 配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use regex::Regex;
fn main() {
// 忽略大小寫:
let re = Regex::new(r"(?i)rust").unwrap();
println!("{}", re.is_match("RUST"));
println!("{}", re.is_match("rust"));
// 多行模式:
// (?m) ^ $ 匹配每行
let m = Regex::new(r"(?m)^go").unwrap();
println!("{}", m.is_match("x\ngo"));
// 點匹配換行:
// (?s) . 匹配 \n
// 無內聯:
// RegexBuilder 可配置
let re = regex::RegexBuilder::new("rust")
.case_insensitive(true)
.multi_line(true)
.build().unwrap();
println!("{}", re.is_match("RUST"));
// 單行:
// .size_limit() 限制複雜度
// 默認 UTF-8,禁用用 bytes
}

19.構建與測試

cargo 構建、單元測試、lint 與發佈配置。

cargo build

構建項目。調試/發佈模式。產物位置。增量編譯。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 調試構建:
// cargo build
// 發佈構建(優化):
// cargo build --release
// 檢查(不生成二進制):
// cargo check
// 產物:
// target/debug/ target/release/
// 指定包:
// cargo build -p mylib
// 增量編譯:
// 未變文件不重編譯
// 功能選擇:
// cargo build --features xxx
// 清理:
// cargo clean
// 運行:
// cargo run
// 直接運行二進制:
// ./target/debug/app
// 構建選項在 Cargo.toml [profile]

單元測試

#[test] 屬性。cargo test 運行。斷言宏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 單元測試:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(1, 2), 3);
}
#[test]
fn test_sub() {
assert!(sub(2, 1) == 1);
}
#[test]
#[should_panic] // 期望 panic
fn test_bad() {
panic!("boom");
}
}
fn add(a: i32, b: i32) -> i32 { a + b }
fn sub(a: i32, b: i32) -> i32 { a - b }
// 運行:
// cargo test
// 運行單個:
// cargo test test_add
// 顯示輸出:
// cargo test -- --nocapture
// 斷言宏:
// assert_eq! / assert_ne!
// assert! 帶消息

文檔測試

/// 文檔註釋可運行。cargo test 驗證示例。集成測試。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// 兩數相加。
///
/// # 示例
/// ```
/// let result = add(1, 2);
/// assert_eq!(result, 3);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// 文檔測試:
// 示例代碼會被編譯運行
// cargo test 驗證
// 生成文檔:
// cargo doc --open
// 集成測試:
// tests/integration.rs
// 直接調用公共 API
// 運行:cargo test
// 文檔註釋語法:
// /// 文檔註釋
// //! 模塊級文檔
// ```rust,ignore 跳過
// ```text 不編譯

依賴管理

Cargo.toml 聲明依賴。版本語義。cargo update。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Cargo.toml 依賴:
// [dependencies]
// serde = "1" // 兼容任意 1.x
// serde = "1.0.100" // 精確下限
// serde = "=1.0.100" // 精確版本
// serde = "^1.2" // 兼容
// 可選依賴:
// [dependencies]
// serde = { version = "1", optional = true }
// 開發依賴:
// [dev-dependencies]
// pretty_assertions = "1"
// 更新:
// cargo update // 更新鎖文件
// cargo add serde // 添加依賴
// cargo remove serde // 移除
// 檢查過期:
// cargo outdated
// 審計漏洞:
// cargo audit

clippy 檢查

cargo clippy 代碼檢查。發現潛在 bug 與風格問題。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 運行檢查:
// cargo clippy
// 嚴格模式:
// cargo clippy -- -D warnings
// 常見檢查:
// 1. 不必要的克隆
// 2. 可簡化寫法
// 3. 性能陷阱
// 4. 可疑邏輯
// 示例問題:
// let v = vec![1, 2];
// if v.len() > 0 {} // clippy 建議
// if !v.is_empty() {} // 更好
// 忽略規則:
// #[allow(clippy::all)]
// 局部允許:
// #[allow(clippy::redundant_clone)]
// 配置文件:
// clippy.toml
// 集成 CI 常用 -D warnings
// 用 & 代替 .iter() 等建議

代碼格式化

cargo fmt 統一格式。rustfmt。團隊一致風格。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 格式化:
// cargo fmt
// 檢查格式(不修改):
// cargo fmt --check
// 配置文件:
// rustfmt.toml
// 常用配置:
// max_width = 100
// tab_spaces = 4
// 特性:
// 自動縮進對齊
// 導入排序(需 nightly):
// cargo +nightly fmt
// CI 檢查:
// cargo fmt --check
// 與 clippy 配合:
// 先 fmt 再 clippy
// 編輯器保存時自動
// 團隊統一風格
// 減少 diff 衝突

發佈配置

[profile.release] 優化配置。裁剪二進制、LTO。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Cargo.toml:
// [profile.release]
// opt-level = 3 // 優化級別
// lto = true // 鏈接時優化
// codegen-units = 1 // 單代碼單元
// panic = "abort" // 直接終止
// strip = "symbols" // 去符號
// debug = false // 無調試信息
// 小體積組合:
// [profile.release]
// lto = true
// strip = "symbols"
// opt-level = "z" // 偏大小
// 構建:
// cargo build --release
// 查看大小:
// ls -lh target/release/app
// 發佈前:
// cargo fmt --check
// cargo clippy -- -D warnings
// cargo test
// 版本號:
// Cargo.toml version 字段

文檔生成

cargo doc 生成 API 文檔。rustdoc 標記。發佈前跑文檔測試。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 生成文檔:
// cargo doc
// 打開瀏覽器:
// cargo doc --open
// 文檔註釋:
/// 計算兩個數的和。
///
/// # 參數
/// - `a`:第一個數
/// - `b`:第二個數
///
/// # 返回
/// 和
///
/// # 示例
/// ```
/// assert_eq!(add(1, 2), 3);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// 文檔常用標記:
// # Examples # Panics
// # Errors # Safety
// 模塊文檔://!
// 內鏈:\[`add`\]
// 私有項不生成
// 發佈前跑 doc 測試

官方鏈接

直達官方文檔與資源。

關於本速查

本頁是 Rust 2021 版的自包含速查手冊,覆蓋語言核心與最常用 std API 在真實項目中約 80% 的常見用法。內容偏向現代慣用法:? 傳播錯誤、match 窮盡匹配、用迭代器替代手寫循環、嚴格區分 String 與 &str。權威參考見 Rust 官方 Book 與標準庫文檔。 19 個章節各自聚焦一個主題——從第一個程序到所有權、trait、併發與常見誤區。每節拆成 8 個帶示例的小節(每個 5–20 行),共約 150 個主題。代碼片段刻意短小、自解釋。 所有處理都在瀏覽器中完成——無上傳、無追蹤。本頁是 GuruToolkit 免費開發者工具集的一部分;代碼片段可自由使用,無任何擔保。

版本 2.1.0