1. 程式人生 > 其它 >Rust學習——所有權概念

Rust學習——所有權概念

一、什麼是所有權
一旦理解了所有權,就不需要經常考慮棧和堆了。

一些語言自帶垃圾回收機制
一些語言需要程式設計師手動分配記憶體和釋放
Rust通過所有權系統管理記憶體,編譯器會在編譯時根據一些列規則進行檢查。在執行時,所有權系統不會減慢程式。

棧(Stack):所有資料必須佔用已知固定的大小
堆(Heap):
1. 大小未知或大小可能變化的資料儲存在堆上
2. 往堆放入資料時,需要請求一定大小的空間,記憶體分配器會在堆的某處找到一塊足夠的空位,標記為已使用,並返回一個表示該位置地址的指標,這個過程叫做分配(allocating)。

入棧比堆分配快:因為入棧時分配器無需為儲存新資料去搜索記憶體空間;且位置總是在棧頂。

訪問堆資料比訪問棧慢:因為必須通過指標來訪問。現代處理器在記憶體中跳轉越少就越快。

處理資料彼此較近建議用棧
處理資料彼此較遠建議用堆

所有權系統解決的問題:
1. 跟蹤哪部分程式碼正在使用堆上資料
2. 最大限度的減少堆上重複資料的數量
3. 清理堆上不再使用的資料確保不會耗盡空間

所有權規則:
1. Rust中每一個值都有一個被稱為其所有者(Owner)的變數
2. 值在任一時刻有且只有一個所有者
3. 當所有者(變數)離開作用域,這個值將被拋棄

String型別:可變、可增長的文字片段,需要在堆上分配一塊在編譯時未知大小

Rust中記憶體在擁有它的變數離開作用域後就被自動釋放

String由三部分組成:一個指向存放字串內容記憶體的指標,一個長度,和一個容量。這一組資料儲存在棧上。右側則是堆上存放內容的記憶體部分。

變數與資料互動的方式(一):移動
1. 二次釋放問題。這個問題會導致記憶體汙染。
2. Rust 禁止你使用無效的引用。

變數與資料互動的方式(二):克隆
哪些型別實現了Copy trait ?
- 所有整型:如u32、i32
- 布林型別
* 所有浮點型別:如f64
* 字元型別,char
* 元組,tuple,當且僅當其包含型別也都實現Copy的時候

所有權與函式
Rust中,向函式傳遞值可能會移動或賦值,就像賦值語句一樣。

變數的所有權總是遵循相同的模式:
將值賦給另一個變數時移動它。當持有堆中資料值的變數離開作用域時,其值將通過drop被清理掉,除非資料被移動為另一個變數所有。

二、引用與借用
引用:(無所有權)
1. &符號,允許使用值但不獲取其所有權
2. 與使用&引用相反的操作時解引用,使用解引用運算子*

借用:
1. 引用的一個行為
2. 在函式體裡,被引用的變數無法修改自身引用的值

可變引用
1. 增加 &mut 標記引用即可改變引用的值
2. 限制:在同一時間只能有一個對某一特定資料的可變引用。(同一時間只能可變引用一次)
3. 上述限制的好處:在編譯時避免資料競爭。
4. 不能在擁有不可變引用的同時擁有可變引用

非詞法作用域生命週期(NLL):
編譯器在作用域結束之前判斷不再使用的引用的能力。

懸垂引用:
指其指向的記憶體可能已經被分配給其他持有者

警告:最好不要將變數的引用通過函式返回值的方式返回

引用總結:
1. 在任意給定時間,要麼只能有一個可變引用,要麼只能有多個不可變引用。
2. 引用必須總是有效的。

三、字串Slices(無所有權)
解決了什麼問題?
A:資料從某個特定狀態計算而來的值,與該狀態完全沒有關聯,導致不相關變數需要保持同步。

let s = String::from("Hello");

let slice = &s[0..2] // -> He
// equally to
let slice = &s[..2] // -> He
let len = s.len();
let slice = &s[3..len];
// equally to
let slice = &s[3..];

字串 Slice 型別宣告標記:“&str”

code eg:
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[..i];
}
}
&s[..]
}

fn main() {
let mut input = String::from("Hello World");
let word = first_word(&input); // 不可變引用
println!("first word is: {}", &word);
input.clear(); // 試圖獲取一個可變引用 input!
println!("sec word is: {}", &word); // 此時又訪問了不可變引用,導致可變與不可變引用同時存在,非法!
}

字串字面值就是Slice型別
1. 如:let s = "hello, world";
2. 此處s型別是 &str,由於 &str 是一個不可變引用,所以字串字面值是不可變的。

其他型別的Slice
陣列型別也有slice,工作方式與字串的一樣,同時儲存第一個集合元素的引用和一個集合的總長度。

學習是一條令人時而喜極若狂、時而鬱鬱寡歡的道路。