跳轉到

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、與所有權系統在語言層面同時解決三者。

相關條目#