Rust 裡沒有 delete
,因為資源會自己走完生命週期
在 C++ 裡,你可以這樣:
string* s = new string("hello");
cout << *s;
delete s; // 忘記就爆
Rust 規則是:
每個值有唯一的擁有者(owner),離開作用域就自動被釋放。
fn main() {
let s = String::from("hello");
println!("{s}");
} // 離開這裡,s 自動 drop()
不用 delete
,因為 Rust 的 RAII(Resource Acquisition Is Initialization)是語言級的強制規範,所有資源(不只是記憶體,還有檔案、socket、鎖)都在離開作用域時自動清理。
C++ 會允許你這樣做:
int* p;
{
int x = 10;
p = &x; // 合法
}
cout << *p; // boom
Rust 直接在編譯期攔下來:
let r;
{
let x = 10;
r = &x; // error: `x` does not live long enough
}
println!("{r}");
因為 Rust 有一套生命週期(lifetime)分析器,能在編譯時判斷指標(實際上是 reference)會不會被懸掛。
這個機制是 Borrow Checker 在背後做的,它的邏輯大致是:
&T
,要嘛只有一個可變借用 &mut T
C++ 的 std::unique_ptr
只是個類型包裝,而 Rust 的 ownership/borrow 則是語法規則。
nullptr
與 throw
C++ 裡常見這樣的錯誤傳遞方式:
Foo* find_user(int id) {
if (id < 0) return nullptr;
return new Foo(id);
}
然後呼叫端忘記檢查 nullptr。
Rust 沒有 null,取而代之的是 Option<T>
:
fn find_user(id: i32) -> Option<Foo> {
if id < 0 { None } else { Some(Foo { id }) }
}
呼叫端一定得處理兩種情況:
if let Some(user) = find_user(10) {
println!("找到使用者 {:?}", user);
} else {
println!("沒找到");
}
錯誤也一樣,用 Result<T, E>
取代 throw
:
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 { Err("除以零".into()) } else { Ok(a / b) }
}
fn main() -> Result<(), String> {
let x = divide(10.0, 2.0)?; // `?` 會自動往外丟 Err
println!("結果 = {x}");
Ok(())
}
而這種傳遞機制就會處理每一個錯誤不會漏掉。
C++ 用繼承表達多型:
class Shape {
public:
virtual void draw() = 0;
};
class Circle : public Shape {
public:
void draw() override { cout << "Circle\n"; }
};
Rust 用 Trait 表達「誰能做什麼」:
trait Drawable {
fn draw(&self);
}
struct Circle;
struct Square;
impl Drawable for Circle {
fn draw(&self) { println!("畫圓"); }
}
impl Drawable for Square {
fn draw(&self) { println!("畫方"); }
}
fn render<T: Drawable>(obj: &T) {
obj.draw();
}
Trait 沒有繼承樹、沒有 vtable 開銷,每個實作在編譯期就展開成具體函式呼叫(zero-cost abstraction)
在 C++ 裡:
std::string s1 = "hi";
std::string s2 = s1; // 呼叫拷貝建構子
Rust 裡:
let s1 = String::from("hi");
let s2 = s1; // move,s1 不可再用
// println!("{s1}"); // error
如果型別很輕(如 i32、f64),則自動 Copy:
let a = 10;
let b = a; // OK,a 繼續可用
Rust 讓每個型別都明確表達「我能被 Copy 嗎?」這樣能避免不小心深拷貝整個結構體或造成記憶體多重釋放。
Rust 它沒有 Garbage Collector,也不需要。釋放時機在編譯期就能決定,這意味著: