所有權(Ownership)#
所有權 是 Rust 最核心、最獨特的特性。
透過所有權機制,Rust 在 不需要垃圾回收器(GC) 的情況下,保證記憶體安全。
1. 所有權的三條規則#
- Rust 中每一個值都有一個 擁有者(owner) 。
- 同一時間 只能有一個擁有者 。
- 當擁有者離開作用域(scope),值就會被 釋放(dropped) 。
2. 作用域(Scope)#
變數在宣告後開始有效,直到所在的區塊({})結束:
1
2
3
4
5
6
| fn main() {
{
let s = String::from("hello"); // s 在此有效
println!("{s}");
} // 此處 s 離開作用域,記憶體被釋放
}
|
3. String 與堆積(Heap)#
Rust 的基本型別(如 i32、bool)大小固定,存放在 堆疊(Stack) 上。
String 型別的內容大小可變,資料存放在 堆積(Heap) 上:
1
| let s = String::from("hello");
|
當 s 離開作用域,Rust 會自動呼叫 drop 函數釋放堆積上的記憶體。
4. 移動(Move)#
將 String 指派給另一個變數時,不會複製資料,而是 移動(move) 所有權:
1
2
3
4
5
6
7
| fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1 的所有權移動給 s2
// println!("{s1}"); // 錯誤!s1 已失效
println!("{s2}"); // 正確
}
|
這樣可以確保同一塊記憶體不會被釋放兩次。
5. 克隆(Clone)#
若需要深度複製堆積上的資料,可以使用 clone():
1
2
3
4
5
6
7
| fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // 深度複製
println!("{s1}"); // 仍有效
println!("{s2}");
}
|
clone() 會複製堆積上的資料,因此 成本較高 。
6. 複製(Copy)#
存放在堆疊上的型別(如整數、浮點數、布林、字元、只含 Copy 型別的元組)實作了 Copy trait,指派時 自動複製 ,不會移動:
1
2
3
4
5
6
7
| fn main() {
let x = 5;
let y = x; // 複製,x 仍有效
println!("{x}");
println!("{y}");
}
|
7. 所有權與函數#
將值傳入函數時,所有權規則同樣適用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| fn main() {
let s = String::from("hello");
takes_ownership(s); // s 的所有權移入函數
// s 在此已失效
let n = 5;
makes_copy(n); // n 是 Copy 型別,僅複製
println!("{n}"); // 仍可使用
}
fn takes_ownership(some_string: String) {
println!("{some_string}");
} // some_string 在此被釋放
fn makes_copy(some_integer: i32) {
println!("{some_integer}");
}
|
8. 回傳值與所有權#
函數也可以透過回傳值來轉移所有權:
1
2
3
4
5
6
7
8
9
10
11
12
13
| fn main() {
let s1 = gives_ownership(); // 取得函數回傳的所有權
let s2 = String::from("hello");
let s3 = takes_and_gives_back(s2); // s2 移入函數,再移回 s3
}
fn gives_ownership() -> String {
String::from("yours")
}
fn takes_and_gives_back(s: String) -> String {
s // 回傳,所有權移出
}
|
每次都要轉移所有權再傳回,非常繁瑣。下一章的 借用(Borrowing) 解決了這個問題。
Reference#
https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html