Rust筆記之智能指針
一、定義
指針: 是一個包含內存地址的變量的通用概念。(Rust中最常見的指針就是引用&T)
智能指針:是一類數據結構,它們的表現類似指針,但也有額外的元數據和功能。
二、智能指針的類型
1. Box <T>
(1) box允許你將一個值放在堆上而不是棧上,留在棧上的則是指向堆數據的指針。
(2) 除了數據被存放在堆上而不是棧上之外,box沒有性能損失,不過也沒有很多的額外功能。
(3) 使用場景:
當有一個在編譯時未知大小的類型,而又想要在需要確切大小的上下文中使用這個類型值的時候;
當有大量數據並希望在確保數據不被拷貝的情況下轉移所有權的時候;
當希望擁有一個值並只關心它的類型是否實現了特定trait而不是具體類型的時候;
(4)特徵:單一所有者,允許在編譯時執行不可變或可變借用檢查。
//解決遞歸類型的使用
//無法編譯通過的例子,原因時編譯器無法算出他所需的大小
enum List{
Con(i32,List),
Nil
}
fn main(){
let list = Con(1,Con(2,Con(3,Nil)));
}
//改進
enum LIst{
Con(i32,Box<List>),
Nil
}
fn main(){
let list = Con(1,Box::new(Con(2,Box::new(3,Nil))));
}
Box<T>類型是一個智能指針,它的值可以被當作引用對待,這是因爲它實現Deref trait,當Box<T>值離開作用域時,由於Box<T>還實現了Drop trait,所以box所指向的堆數據也會被清除。
2. Rc <T>引用計數智能指針
(1) 使用場景:當我們希望在堆上分配一些內存供程序的多個部分去讀取,而且無法在編譯時確定程序的哪一部分會最後結束使用它的時候。
(2)Rx<T>只能用於單線程場景。
(3)特徵:允許相同數據有多個所有者,在編譯時執行不可變借用檢查。
use std::rc::Rc;
enum List{
Con(i32,Rc<List>),
Nil
}
fn demo4(){
let a = Rc::new(List::Con(5,Rc::new(List::Con(10,Rc::new(List::Nil)))));
let b = List::Con(3,Rc::clone(&a));
let c = List::Con(4,Rc::clone(&a));
//打印引用計數,結果應該是3
println!("the Rc count is {}",Rc::strong_count(&a));
}
3. RefCell <T>
(1) 作用:RefCell<T>用於當你確信代碼遵守借用規則,而編譯器不能理解和確定的時候。
(2)特徵:單一所有者,在運行時執行不可變或可變借用檢查。
三、使用的trait
1. Deref trait
(1) 作用:實現Deref trait允許我們重載解引用運算符‘*’;通過這種方式實現Deref trait的智能指針可以被當作常規引用來對待,可以編寫操作引用的代碼並用於智能指針。
//解決自定義類型提示的‘cannot be dereferenced’問題
#struct MyBox<T> (T);
#impl MyBox<T> {};
use std::ops::Deref;
impl<T> Deref for MyBox<T> {
type Target = T;//定義了用於此trait的關聯類型
fn deref(&self) -> &T{//不用顯示引用,在編譯*y時,編譯器會拓展爲*(y.deref())
&self.0
}
}
2. Drop trait
(1) 作用:Drop trait 允許我們在值要離開作用域時執行一些代碼,所執行的代碼一般被用於釋放類似文件或網絡連接等資源。在Box<T>中,就是執行了釋放堆內存的代碼。
//演示drop
struct CustomSmartPointer{
data : String,
}
impl Drop for CustomSmartPointer{
fn drop(&mut self){
println!("Dropping CustomSmartPointer with data {}",self.data);
}
}
fn main() {
//derefDemo();
let c = CustomSmartPointer{
data:String::from("my stuff")
};
println!("CustomSmartPointer created");
}
//打印結果如下:
CustomSmartPointer created
Dropping CustomSmartPointer with data my stuff
(2) 注意:Rust不允許我們主動調用Drop trait的drop方法,當我們希望在作用域結束之前就強制釋放變量的話,我們應使用的是由標準庫提供的std::mem::drop
//演示drop
struct CustomSmartPointer{
data : String,
}
impl Drop for CustomSmartPointer{
fn drop(&mut self){
println!("Dropping CustomSmartPointer with data {}",self.data);
}
}
fn main() {
//derefDemo();
let c = CustomSmartPointer{
data:String::from("my stuff")
};
println!("CustomSmartPointer created");
drop(c);
println!("CustomSmartPointer end");
}
//打印結果如下:
CustomSmartPointer created
Dropping CustomSmartPointer with data my stuff
CustomSmartPointer end
四、存在的問題
1. 引用循環與內存泄漏
(1) 爲了避免引用循環,在循環引用中,可以將其中一個方向替換成Weak<T>。將Rc的強引用爲0,而弱引用不爲0時,仍然可以情況該對象,這時我們就要藉助Weak<T>的upgrade()方法來判斷引用對象是否有效。
//生成Weak<T>
let w = Rc::downgrade(&Rc<T>);
//判斷若引用對象是否存在
let result = w.upgrade;//返回一個Option<T>的值