平臺電商網站開發(fā)5月疫情最新消息
Rust中的智能指針
- 什么是智能指針?
- 什么是Rust中的智能指針?
- Rust中的智能指針Box
- Box的使用場景
- Rust中的智能指針Rc與Arc
- rust中的RefCell
- refcell的缺點:
- rust中的weak
- 先來看看C++中的weak_ptr定義
- 代碼示例:
- Deref和Drop
- 總結
什么是智能指針?
傳統(tǒng)的指針,如C++中的裸指針,需要開發(fā)者自己申請和釋放,如果開發(fā)者在使用過程中疏漏了回收,將會造成內存泄漏,在部署
實施時也會有oom的風險,智能指針即是為了解決這類問題而出現(xiàn)的,老生常談的shared_ptr,unique_ptr,weak_ptr等,都是通過設計使得代碼自動管理回收堆上的內存,提高代碼的健壯性和方便性。
什么是Rust中的智能指針?
Rust由于其嚴格的安全性和所有權機制,除unsafe寫法外,所有的堆內存都是通過語言特性管理的。其目的和其他語言一致都是為了健壯性和方便使用,與C++的智能指針使用的機制并無二致,都是使用了RAII(即資源獲取即初始化),常見的rust智能指針有Box,Rc,Arc,Weak等。(個人理解:因為rust中的智能指針常與引用符號&結合使用,與其叫做智能指針,不如叫智能引用。)
Rust中的智能指針Box
首先看一下C++中的unique_ptr:
1.unique_ptr所指向(引用)的資源只能被unique_ptr所獨占,不能被Copy,只能被轉移。
2.自動析構不計數(shù)(結合一中的定義,獨占型指針也沒有任何計數(shù)的必要)
以上是unique_ptr的特點,這也是Rust中Box的主要特點:
只能轉移所有權,不能Copy,同時只能有一個有效的Box。
Box的使用場景
由于Box的獨占特點,其使用一般可以用在:
- 避免深拷貝一些多字節(jié)的數(shù)據(jù)
- 此文中提到的特征對象作為返回值使用,個人理解這是Box的最有用且最常用之處。
- 作為容器中得item如:
vec![Box<dyn Noise>] //比較適合在實踐一些設計模式時使用
代碼示例:
fn returns_noise(isdog: bool) -> Box<dyn Noise> {if isdog {Box::new(Dog {voice: String::from("wangwang",),})} else {Box::new(Cat {voice: String::from("miaomiao",),})}
}
Rust中的智能指針Rc與Arc
Rc: 全稱 Reference Count,即引用計數(shù)。
Arc: 全稱Atomic Reference Count,即原子性引用計數(shù)。
由定義可知,Arc對比與Rc的一大優(yōu)點就是原子性,既線程安全。而實現(xiàn)了線程安全勢必要損失了一些性能,所以Rc比Arc性能要更好些,這兩個智能指針都是只讀的 。
Rc與C++中的Shared_ptr機制類似,都是通過引用計數(shù)和RAII最終實現(xiàn)對于堆內存的自動控制,兩者都是線程不安全的,最大的區(qū)別便是Rc只讀。
代碼示例
{ let rc1 = Rc::new(String::from("hello world"));let rc2 = Rc::clone(&rc1);let rc3 = Rc::clone(&rc1);
}{ let mystr = String::from("hello world");let bx2 = Box::new(&mystr);let bx3 = Box::new(&mystr);//got error
}
以上是Rc與Box間的對比,由于所有權的轉移bx3在二次借用時便會出錯,而rc擁有計數(shù)規(guī)則,上述代碼將通過編譯。
這里細心的同學會留意到clone,這里只是淺拷貝。
原則上,棧上數(shù)據(jù)基本都可以直接復制,而堆上內存申請性能相對較慢,堆上內存非必要情況下不做深拷貝,同理,如果你作為一個語言開發(fā)者,非必要情況下也不會默認將堆上內存直接深拷貝。
Arc是Rc的線程安全版本,用法函數(shù)基本一致,不做代碼示例,有需要可用自行查閱。
rust中的RefCell
在之前我們提到過Rc與C++中的shared_ptr很接近,但是是只讀的,如何做到內部可變 —> 結合RefCell。
內部可變:在不改變外部套殼的情況下,可更改內部數(shù)值。
let s = Rc::new(RefCell::new("hello ".to_string()));let s1 = s.clone();let s2 = s.clone();s2.borrom_mut().push_str("world");println!("{:?}",s);println!("{:?}",s1);println!("{:?}",s2);
均打印出 hello world
refcell的缺點:
代碼使用了refcell后,rust被遵循的借用三大規(guī)則被移動到運行時,強制panic(也算比較安全,至少比unsafe從字面上的來看更舒服些)
rust中的weak
先來看看C++中的weak_ptr定義
std::weak_ptr is a smart pointer that holds a non-owning (“weak”) reference to an object that is managed by std::shared_ptr. It must be converted to std::shared_ptr in order to access the referenced object.
std::weak_ptr models temporary ownership: when an object needs to be accessed only if it exists, and it may be deleted at any time by someone else, std::weak_ptr is used to track the object, and it is converted to std::shared_ptr to acquire temporary ownership. If the original std::shared_ptr is destroyed at this time, the object’s lifetime is extended until the temporary std::shared_ptr is destroyed as well.
Another use for std::weak_ptr is to break reference cycles formed by objects managed by std::shared_ptr. If such cycle is orphaned (i.e., there are no outside shared pointers into the cycle), the shared_ptr reference counts cannot reach zero and the memory is leaked. To prevent this, one of the pointers in the cycle can be made weak.
總之,weak_ptr很弱,只記錄狀態(tài)信息,不保證一定存在,同時(主要)為了解決shared_ptr造成的循環(huán)引用,通常也不會單獨出現(xiàn),必須要轉換成shared_ptr.
對比rust中的weak也是幾乎一樣的設計理由和使用條件,不保證引用一定存在,所以它返回Option< Rc < T > >,代碼寫法即upgrade升級到Rc,或將Rc降級到weak。
代碼示例:
use std::rc::{Rc, Weak};
use std::cell::RefCell;
#[derive(Debug)]
struct Node {value: i32,parent: RefCell<Weak<Node>>,children: RefCell<Vec<Rc<Node>>>,
}fn main() {let _leaf = Rc::new(Node {value: 3,parent: RefCell::new(Weak::new()),children: RefCell::new(vec![]),});
}
Deref和Drop
- Deref: 將引用中的實際值解出并使用,是Rust中最常見的隱式轉換,如將String類型傳入 &str入參時等等
- Drop: 釋放資源,類似于析構函數(shù),同樣的,有默認實現(xiàn),也有主動重寫。
- Deref 是特征,一般開發(fā)者僅會為自定義的智能指針實現(xiàn)解引用特征。
- 解引用可遞推,所以在隱式轉換時常常有多層的解引用。
總結
本章結合題目非常適合鏈表練習。
如有勘誤,敬請指出。