所有權可以說是Rust核心概念,這讓Rust不需要垃圾回收(garbage collector)就可以保障記憶體安全。Rust的安全性和所有權的概念息息相,因此理解Rust中的所有權如何運作是非常重要的
用下面這段程序描述變數范圍的概念
{
// 在宣告以前,變數s無效
let s = "hello";
// 這裡是變數s的可用範圍
}
// 變數範圍已經結束,變數s無效
變數作用域是變數的一個屬性,其代表變數的可使用範圍,默認從宣告變數開始有效直到變數所在作用域結束。
定義一個變數並賦予值,這個變數的值存在記憶體中,例如需要用戶輸入的一串字串由於長度的不確定只能存放在堆(heap)上,這需要記憶體分配器在執行時請求記憶體並在不需要時還給分配器
在擁有垃圾回收機制(garbage collector, GC)的語言中,GC會追蹤並清除不再使用的記憶體,如果沒有GC的話則需要在不使用時顯式的呼叫釋放記憶體
例如C語言
{
char *s = strdup("hello");
free(s); *// 釋放s資源*
}
Rust選擇了一個不同的道路,當變數在離開作用域時會自動釋放例如下面
{
let s = String::from("hello"); // s 在此開始視為有效
// 使用 s
} // 此作用域結束,釋放s變數
當變數離開作用域(大括號結束)時會自動呼叫特殊函示drop來釋放記憶體
移動(Move)
變數可以在Rust中以不同的方式與相同的資料進行互動
let x = 100;
let y = x;
這個代碼將值100綁定到變數x,然後將x的值復制並賦值給變數y現在棧(stack)中將有兩個值100。此情況中的數據是"純量型別"的資料,不需要存儲到堆中,僅在棧(stack)中的資料的"移動"方式是直接複製,這不會花費更長的時間或更多的存儲空間。"純量型別"有這些:
現在來看一下非純量型別的移動
let s1 = String::from("hello");
let s2 = s1;
String物件的值"hello"為不固定長度長度型別所以被分配到堆(heap)
當s1賦值給s2,String的資料會被拷貝,不過我們拷貝是指標、長度和容量。我們不會拷貝指標指向的資料
前面説當變數超出作用域時,Rust自動調用釋放資源函數並清理該變數的記憶體。但是s1和s2都被釋放的話堆(heap)區中的"hello"被釋放兩次,這是不被系統允許的。為了確保安全,在給s2賦值時 s1已經無效了
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1); // 會發生錯誤 s1已經失效了
克隆(clone)
正常情況下Rust在較大資料上都會以淺拷貝的方式,當然也有提供深拷貝的method
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{} {}", s1, s2);
輸出
hello hello