Rust智慧指標——Box
在C++11中也有智慧指標shared_ptr,unique_ptr,shared_ptr,在Rust中也有叫智慧指標的東西,今天我們來討論一下Box。現在我們要構建一個二叉樹。在Python中實現是比較簡單的:
class Tree: def __init__(self, root_val, left = None, right = None): self.root = root_val self.left = left self.right = right t = Tree(12, Tree(10, None, Tree(14)), Tree(16, Tree(15), Tree(22))))
最終的樹形結構:
12 /\ 1016 \/ \ 141522
現在用Rust來實現一個?
struct Tree { root: i64, left: Tree, right: Tree, }
在上一篇我們已經提到了,上面的程式碼Rust肯定不會讓我們編譯通過。已經提示我們使用&
、Box
、Rc
。他們都是指標型別,都是指向記憶體上某一個位置。
-
&
在Rust裡面稱作borrow型別。只是引用記憶體上某個位置的值,並不擁有所指的值。如果我們想使用&
來修復這個問題,我們需要注意一個問題就是生命週期,borrow型別生命週期取決owner的生命週期。
struct Tree<'a> { root: i64, left: &'a Tree<'a>, right: &'a Tree<'a>, }
-
Box
是一種智慧指標,零執行時開銷。擁有它所指向的資料。我們為什麼稱做智慧的,是因為當執行過邊界,它將drop掉它所指向的資料然後drop本身。不需要手動管理記憶體!!!
struct Tree { root: i64, left: Box<Tree>, right: Box<Tree>, }
-
Rc
是另外一種智慧指標,是reference count
的簡稱。用於記錄資料結構被引用的次數。當引用的數字減小到0時,就自行清理。如果在一個執行緒,對於同一個資料有多個owner的時候,我們選用Rc
。對於多執行緒,我們有Arc
(atomic reference count)
struct Tree { root: i64, left: Rc<Tree>, right: Rc<Tree>, }
上面的三個方法都可以解決之前的問題,那我們該怎麼選擇呢,這取決於自己的使用場景。
Make subtrees optional
緊接著我們又面臨的問題就是無法例項化之前定義的tree結構。為什麼呢?現有的資料結構,left和right子樹都是BoxNone
,但在Rust中有Option
:
struct Tree { root: i64, left: Option<Box<Tree>>, right: Option<Box<Tree>>, }
現在我們就可以建立我們第一個tree:
Tree { root: 12, left: Some(Box::new(Tree { root: 10, left: None, right: Some(Box::new(Tree { root: 14, left: None, right: None, })), })), right: Some(Box::new(Tree { root: 16, left: Some(Box::new(Tree { root: 15, left: None, right: None, })), right: Some(Box::new(Tree { root: 22, left: None, right: None, })), })), };
現在將程式碼做一些優化:
#[derive(Default)] struct Tree { root: i64, left: Option<Box<Tree>>, right: Option<Box<Tree>>, } impl Tree { fn new(root: i64) -> Tree { Tree { root: root, ..Default::default() } } fn left(mut self, leaf: Tree) -> Self { self.left = Some(Box::new(leaf)); self } fn right(mut self, leaf: Tree) -> Self { self.right = Some(Box::new(leaf)); self } }
Tree::new(12) .left( Tree::new(10) .right(Tree::new(14)) ) .right( Tree::new(16) .left(Tree::new(15)) .right(Tree::new(22)) );
那為什麼在Python就能完美執行呢?Python在執行的時候為樹物件動態分配記憶體,將所有內容包裝在PyObject中,類似Rc。
遇到類似的情況我們該怎麼處理和選擇呢?我們需要了解所有可能的解決方案,如果可以,我們就遠離使用智慧指標,堅持簡單借用。如果不能,我們就選擇侵入性更小的一個。