2015:Rust 1.0 問世——所有權、借用與生命週期定型#
事件#
2015 年 5 月 15 日,Rust 1.0 正式發佈,並承諾向後相容(backwards compatibility):穩定的語言特性永不再改動語義。
為何重要#
- 核心語法就此定型:
- 所有權系統:每個值有唯一所有者;賦值與傳參預設是移動(move)而非複製。
- 借用檢查器:
&T(共用借用)與&mut T(可變借用)二分,規則是「可有多個唯讀借用,或恰好一個可變借用」——編譯期杜絕資料競爭與迭代器失效。 - 生命週期標註:
'a讓含引用的型別可以靜態表達「引用的壽命」,補足借用檢查跨函式的推論。 - trait 系統:類似型別類(type class)的多型,
impl Trait for Type;derive自動生成常用實作。 - 模式比對與 enum:代數資料類型 +
match窮盡性檢查,強制處理所有情況(包括Option::None)——直接在語言層面消滅空指標錯誤。 - 宏(macro_rules!):衛生宏(hygienic macro),編譯期程式碼生成。
- 穩定承諾的意義:1.0 之前語言每月大改;1.0 之後企業(Firefox、Docker 生態)才敢投入生產。
理論與實用原因#
- 理論原因:借用規則源自仿射型別理論(affine types, Wadler 1990)與 Cyclone 的 region 型別;「共用 vs 可變」二分來自別名分析(aliasing analysis)——別名與可變性不可同時存在,即可靜態證明安全。
- 實用原因:Docker 早期元件、Firefox 的 Servo 都需要無 GC 的記憶體安全;
match窮盡性檢查則消除 null 參考這類「十億美元的錯誤」。
程式範例#
所有權、借用與生命週期——1.0 的核心語法:
// 所有權:賦值預設是 move
let s1 = String::from("hello");
let s2 = s1; // s1 的所有權移動給 s2
// println!("{}", s1); // 編譯錯誤:borrow of moved value
// 借用:&T 可共用,&mut T 獨佔——兩者不可同時存在
fn len(s: &String) -> usize { s.len() } // 唯讀借用,不改變所有權
let mut v = vec![1, 2, 3];
let first = &v[0];
// v.push(4); // 編譯錯誤:cannot borrow `v` as mutable
// because it is also borrowed as immutable
println!("{first}"); // 借用結束後才能再 push
生命週期標註——含引用的型別,靜態表達壽命:
// 'a 表示:回傳的引用,壽命不超過兩個輸入中較短的那個
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
enum + match 窮盡性檢查——消滅空指標:
enum Shape {
Circle(f64),
Rect(f64, f64),
}
fn area(s: &Shape) -> f64 {
match s {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rect(w, h) => w * h,
// 不寫就編譯錯誤:non-exhaustive patterns——強制處理所有情況
}
}
// Option<T> 讓「可能沒有值」成為型別,null 錯誤在編譯期被消滅
fn find(v: &[i32], target: i32) -> Option<usize> {
v.iter().position(|&x| x == target)
}
let idx = find(&[1, 2, 3], 2);
// println!("{}", idx); // 編譯錯誤:Option<usize> 不是 usize
println!("{:?}", idx); // Some(1)——必須先處理 None
trait 與 derive——自動生成常用實作:
#[derive(Debug, Clone, PartialEq)]
struct Point { x: i32, y: i32 }
trait Area { fn area(&self) -> f64; }
impl Area for Point { fn area(&self) -> f64 { 0.0 } }
對照:Java 的 null 是「十億美元的錯誤」——任何物件參考都可能突然是 null,只有執行期會爆:
String name = map.get("key"); // 可能是 null,編譯器不會提醒
int n = name.length(); // NullPointerException,執行期才爆
彌補了什麼缺陷#
彌補了 C/C++「編譯器無法檢查記憶體壽命與並行安全」的缺陷,也彌補了 Java「null 參考、無窮盡性檢查、GC 不可預測」的缺陷——Rust 用 Option<T>、match、與所有權系統在語言層面同時解決三者。