學 Rust 的人,大多在某個時刻被它的編譯器「教育」過。那個讓你崩潰的東西,就是 Borrow Checker。它不是程式跑起來才發現問題的工具,而是在你按下編譯鍵的瞬間,就會審問你:
這段程式碼,真的安全嗎?
你的記憶體,在這個時間點,誰擁有、誰借用、誰要歸還?
在 C 或 C++ 裡,你可以自由存取記憶體、釋放指標,但自由也代表風險 → dangling pointer、double free、或 memory leak 都可能在你不知道的情況下發生。
Rust 的哲學是:
「我不信任任何工程師喔 :)」
所以它建立一套「編譯期安全審查制度」── 讓程式在能執行之前,先保證記憶體不會出錯。
這個制度的核心,就是 ownership 與 borrowing。
在 Rust 中,每個值都有「唯一的擁有者」,當擁有者離開作用域(scope)時,這個值會被自動釋放。
fn main() {
let s = String::from("hello");
} // 這裡 s 離開作用域,記憶體自動釋放
沒有 free()
、沒有 GC,卻保證安全。
但問題來了——如果我想暫時借用這個變數,不該被釋放,該怎麼辦?
這就進入下一個規則:borrowing。
Rust 的借用系統只有兩種形式:
同時擁有可變與不可變借用會被禁止,
因為 Rust 要確保「有人在改,就不能有人在看」。
fn main() {
let mut v = vec![1, 2, 3];
let r1 = &v; // 不可變借用
let r2 = &mut v; // error:同時存在可變與不可變引用
println!("{:?}", r1);
r2.push(4);
}
錯誤訊息可能會說:
cannot borrow `v` as mutable because it is also borrowed as immutable
這是 borrow checker 在保護你:
它寧願拒絕編譯,也不冒 data race 的風險。
Rust 不是不讓你改,而是要你清楚證明作用域不重疊。
let mut v = vec![1, 2, 3];
{
let r1 = &v;
println!("{:?}", r1);
} // r1 在這裡結束
let r2 = &mut v;
r2.push(4);
split_at_mut()
切開資料區間這樣 Rust 能確認兩個區塊不重疊。
let mut data = [0, 1, 2, 3, 4, 5];
let (a, b) = data.split_at_mut(3);
a[0] = 10;
b[0] = 20;
你不是在說服編譯器「相信你」,
而是提供形式化的證據:
「這兩段記憶體真的不會互相踩到。」
有時候,你真的需要在編譯期繞過檢查,例如:在物件導向模式或資料快取時。
這時可以使用 RefCell<T>
或 UnsafeCell<T>
。
use std::cell::RefCell;
let x = RefCell::new(0);
{
let mut b = x.borrow_mut();
*b += 1;
}
println!("{}", x.borrow());
這樣做會讓「借用檢查」延後到執行期。但若你同時 borrow 兩次,Rust 會在 runtime panic。
Borrow Checker 不像其他語言的垃圾回收器,它不幫你收,也不幫你修。
它只負責一件事:
「我不相信任何人,除非你能證明你是安全的。」
這聽起來苛刻,但它的嚴格換來的結果是:
今天先這樣,昨天趕完這禮拜的作業數了一下我還有六份作業,真不給人活。下禮拜開考期中 哎