1.2 Rust 核心心智模型#
學 Rust 之前,先建立三個心智模型#
Rust 的語法細節很多,但底層只有三個核心觀念。先建立它們,後面的語法都只是這三個觀念的展開:
- 所有權(Ownership):每塊記憶體都有唯一的主人,主人離開作用域,記憶體自動釋放(第三章)
- 無空值(No null):用
Option<T>型別表達「可能沒有值」,編譯器逼你處理 - 無例外(No exception):用
Result<T, E>型別表達「可能失敗」,編譯器逼你處理
心智模型一:所有權代替 GC 與手動釋放#
傳統兩條路:C/C++ 手動 malloc/free(容易漏放、重放),Java/Go 靠 GC(執行時期開銷)。Rust 走第三條路:
每個值有唯一的主人。主人(變數)離開作用域時,值自動釋放——像 C++ 的 destructor,但由編譯器強制保證只有一個主人,於是「double free」「use-after-free」在語言層面不可能發生。
fn main() {
let s = String::from("hello"); // s 擁有這個字串
takes_ownership(s); // 所有權「搬走」了,s 之後不能用
// println!("{}", s); // ← 編譯錯誤!s 已經沒有所有權
} // s 已被搬走,main 結束不需要釋放
fn takes_ownership(s: String) {
println!("{}", s);
} // s 離開作用域,記憶體在這裡自動釋放
心智模型二:Option<T> 代替 null#
Tony Hoare 稱 null 為「十億美元的錯誤」。Rust 完全沒有 null。「可能沒有值」用型別表達:
fn find_user(id: u32) -> Option<String> { // 可能找到,也可能沒有
// ...
}
fn main() {
let user = find_user(42);
println!("{}", user.to_uppercase()); // ← 編譯錯誤!你還沒處理「沒有值」的情況
match user {
Some(name) => println!("找到了:{}", name),
None => println!("查無此人"),
}
}
型別系統記住了「這可能沒有值」,編譯器逼你在編譯期處理——null 例外從此不存在於執行期。
心智模型三:Result<T, E> 代替例外#
Rust 沒有 try/catch。「可能失敗」用型別表達,錯誤是回傳值,不是控制流:
use std::fs::File;
fn main() {
let f = File::open("config.txt");
match f {
Ok(file) => println!("開啟成功"),
Err(e) => println!("失敗:{}", e), // 編譯器逼你處理失敗
}
}
這三個型別(Option、Result、以及背後的 enum,見第四章)構成了 Rust 錯誤處理的全部。
三個模型合作的結果#
flowchart TD
A[所有權<br/>記憶體自動管理] --> R[程式正確性由編譯器保證]
B[Option<br/>沒有空值] --> R
C[Result<br/>錯誤是回傳值] --> R
R --> D[執行期沒有<br/>空指標 / 記憶體錯誤 / 未處理的例外]工程上的意義:Rust 程式碼重構起來特別安心——改了型別或搬了程式碼,編譯器會把所有受影響的地方(包括你沒想到的)全部指出來。這是大型專案長期演進最珍貴的特性。
本章小結#
- 三個心智模型:所有權、Option(無 null)、Result(無例外)
- 所有權:每個值唯一主人,離開作用域自動釋放
- 「可能沒有值」與「可能失敗」都用型別表達,編譯器逼你處理
- 結果:錯誤在編譯期被抓,重構特別安心
想一想#
- 你寫過的語言中,null 和例外造成過什麼問題?Rust 如何在編譯期解決?
- 「錯誤是回傳值,不是控制流」對程式可讀性有什麼影響?
- 為什麼「編譯器逼你處理」反而讓開發更快?(提示:除錯時間的轉移)