今天我們將要介紹 Rust 最核心的關鍵:所有權系統, Rust 並沒有垃圾回收(Garbage Collection),通常情況下會導致記憶體洩露,但是 Rust 的所有權系統讓記憶體洩露的發生率降到極低。
Rust 的所有權有三個重點
有點抽象,很難理解?我們搭配所有權轉移的介紹,希望大家能夠更直接理解所有權的概念。
何時會發生所有權轉移?
賦值給另一個變數
fn main() {
let s1 = String::from("Hello");
let s2 = s1; // s1 的所有權轉移到 s2,s1 失去所有權
// println!("{}", s1); // 拿到註解後,將發生編譯錯誤!因為 s1 已失去資料所有權
println!("{}", s2); // 將顯示 Hello
}
傳遞參數給函數
fn take_ownership(s: String) {
println!("{}", s); // 顯示 Hello
} // s 離開作用域後,資料將失效
fn main() {
let s1 = String::from("Hello");
take_ownership(s1); // s1 傳遞給函數參數,所有權已被轉移
// println!("{}", s1); // s1 已失去資料所有權,去除註解後,將發生編譯錯誤
}
從函數回傳值
fn create_string() -> String {
let s = String::from("Hello");
s // 從函數返回值,所有權將轉移
}
fn main() {
let s1 = create_string(); // 接收回傳的值,並取得所有權
println!("{}", s1); // 顯示 Hello
}
有些時候我們只是想要使用數值,但是不想轉移所有權時,就需要借用和引用。
不想轉移擁有權,只是想借用資料來讀取時,就需要採用不可變借用的方式。
範例:
fn calculate_length(s: &String) -> usize {
s.len() // 可以讀取,但不能修改
}
fn main() {
let s1 = String::from("Hello");
let len = calculate_length(&s1); // 借用 s1, s1 不會失去所有權
println!("'{}'的長度是 {}", s1, len); // 正常輸出 s1 的內容
}
我們想要修改借用的數值時,就需要加上 mut ,來完成可變借用。
範例:
fn change_string(s: &mut String) {
s.push_str(", World!");
}
fn main() {
let mut s = String::from("Hello");
change_string(&mut s); // 可變借用
println!("{}", s); // 輸出:Hello, World!
}
借用時需要特別注意,多個不可變借用可以同時存在,但是當宣告可變借用後,先前的不可變借用將失效。
fn main() {
let mut s = String::from("Hello");
// 多個不可變借用可以同時存在
let s1 = &s;
let s2 = &s;
println!("{}", s1);
println!("{}", s2);
// 可變借用,先前的不可變借用將失效
let s3 = &mut s;
s3.push_str(" World");
// println!("{}", s1); // 編譯錯誤,因為 s1 已經失效
let s4 = &s;
println!("{}", s4); // 正常,顯示 Hello World
}