Rust 為了避免記憶體錯誤,引入所有權 (ownership) 系統。
Rust 的所有權有三大規則:
1. 作用域與釋放
fn main() {
{
let s = String::from("hello");
println!("{}", s);
}
// s 在這裡超出作用域,自動釋放記憶體
}
輸出:
hello
s 在區塊結束後自動被回收,無需手動 free。
2. Move(移動)
fn main() {
let s1 = String::from("hello");
let s2 = s1; // 所有權轉移
println!("{}", s2);
// println!("{}", s1); // ❌ 編譯錯誤,因為 s1 的所有權已被移走
}
輸出:
hello
在這裡,s1 的值移動到 s2,s1 失效。這和 C++ 的淺拷貝不同。
3. Clone(深拷貝)
如果想要保留兩份資料,可以用 .clone():
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
}
輸出:
s1 = hello, s2 = hello
這樣 s1 和 s2 各自持有一份資料。
4. Copy(複製)
基礎型別(整數、浮點數、布林、字元等)不需要 clone,因為它們在賦值時是「複製 (copy)」,而不是「移動 (move)」。
fn main() {
let x = 5;
let y = x; // 這裡是 copy,不是 move
println!("x = {}, y = {}", x, y);
}
輸出:
x = 5, y = 5
這是因為基礎型別存在 stack 上,複製代價很小。
5. Rust vs C++:深拷貝的差別
在 C++ 中,是否是「深拷貝」取決於有沒有實作複製建構函式 (copy constructor)。預設的行為是「淺拷貝」,如果類別內含指標,可能造成重複釋放或懸空指標。若要「深拷貝」,必須自己寫 copy constructor,確保額外配置記憶體並複製內容。在 Rust 中,則更單純,預設是移動 (move),原本的變數會失效;如果需要深拷貝,就必須明確呼叫 .clone()。這樣的區分讓程式行為不會產生模糊空間。
6. 學習心得與補充
今天最大的收穫是第一次真正理解淺拷貝和深拷貝的差別。以前在寫 C/C++ 的時候,雖然知道變數之間可以互相指定,但我並沒有仔細想過它背後是共用同一份資料,還是重新開一塊新的記憶體。今天學到這個概念後,再回頭看 Rust 的 .clone(),就覺得特別清楚又方便。和前幾天學到的變數不可變性、shadowing 甚至型別推導相比,這些規則其實都在往同一個方向走,讓我在寫程式的時候更精確地表達變數是否可改、型別應該是什麼、資料是否需要複製。我覺得這樣的學習過程很有意思,因為每一天學到的新東西都能和之前的內容連起來,慢慢把 Rust 的思維方式拼湊完整。