Rust學習筆記(四十五)智慧指標,使用Box指向堆上的資料

智慧指標相關概念

指標:是一個包含記憶體地址的變數(指向其它資料)。Rust中最常見的指標是”引用”。 引用(借用):

使用&

借用它指向的值

沒有其餘開銷

是最常見的指標型別

智慧指標

智慧指標是這樣一些資料結構:

行為和指標相似

有額外的元資料和功能

引用計數(reference counting)智慧指標型別

透過記錄所有者的數量,使一份資料被多個所有者同時持有

並在沒有任何所有者時自動清理資料

引用和智慧指標的區別

引用:只是借用資料

智慧指標:很多時候都擁有它所指向的資料

智慧指標的例子

例如String和Vec

都擁有一片記憶體區域,且允許使用者對其操作

擁有元資料(例如容量等)

提供額外的功能或保障(String保障其資料是合法的UTF-8編碼)

智慧指標的實現

智慧指標通常使用struct實現,並且實現了:

Deref trait

Drop trait

Deref trait:允許智慧指標struct的例項像引用一樣實現 Drop:允許我們自定義當智慧指標例項走出作用域時執行的程式碼

標準庫中常見的智慧指標:

Box:在堆記憶體上分配值

Rc:啟用多重所有權的引用計數型別

Ref和RefMut,透過RefCell訪問:在執行時而不是編譯時執行借用規則的型別

此外:

內部可變模式(interior mutability pattern):不可變型別暴露出可修改其內部值的API

引用迴圈(reference cycles):它們如何洩露記憶體,以及如何防止其發生

使用Box來指向堆上的資料

Box是最簡單的智慧指標:

允許我們在堆上儲存資料(而不是棧上)

棧上存的是指向堆記憶體資料的指標

沒有效能開銷

沒有其它額外的功能

實現了Deref和Drop trait

Box的常用場景

在編譯時,某型別的大小無法確定。但使用該型別時,上下文卻需要知道它的確切大小

當我們有大量資料,想移交所有權,但需要確保在操作資料時不會被複制

使用某個值時,我們只關心它是否實現了特定的trait,而不關心它的具體型別

使用Box在堆記憶體上儲存資料

fn main() { let a = Box::new(6); println!(“a = {}”, a);}

當a走出作用域時,它在堆記憶體上的資料和棧上的指標都會被釋放。

Box賦能遞迴型別

在編譯時,Rust需要知道一個型別所佔的空間大小。而遞迴型別(如圖所示)的大小無法在編譯時確定。

Rust學習筆記(四十五)智慧指標,使用Box指向堆上的資料

但是Box型別的大小確定,所以在遞迴型別中使用Box就可以解決上述問題。 這種型別類似於函式式語言中的Cons List。

關於Cons List

Cons List是來自Lisp語言的一種資料結構,其中的每個成員都由兩個元素組成:

當前項的值,例如圖上的i32

下一個元素,是它本身

Cons List裡最後一個成員只包含一個Nil值,沒有下一個元素。

但是在Rust中,Cons List不是常用的集合,通常情況下,Vec是更好的選擇。下面我們在Rust中建立一個Cons List。

使用Box來獲得確定大小的遞迴型別

enum List { Cons(i32, Box), Nil,}fn main() { let list = List::Cons( 1, Box::new(List::Cons(2, Box::new(List::Cons(3, Box::new(List::Nil))))), );}

Box是一個指標,Rust知道其所佔空間大小,因為指標的大小不會基於它指向資料的大小變化而變化。 Box

只提供了“間接”儲存和堆記憶體分配的功能

沒有提供其它額外的功能

沒有效能開銷

適用於需要“間接”儲存的場景,如Cons List

實現了Deref和Drop兩個trait