Rust 智能指針(一)
1.Box<T>
Box<T>是指向堆中的指針。
fn main() {
let box = Box::new(3);
println!("{}", box);
}
在出了指針的作用域之后,指針和它指向的對象都將被釋放。
在本例中,box將在main函數之后被釋放。
由於Box<T>的大小是確定的(size類型的大小),所以可以使用Box編寫嵌套類型,比如實現鏈表。
2.Deref trait
實現Deref這個trait可以重載解引用運算符(*),這樣可以把Deref trait當作普通引用。
use std::ops::Deref;
fn main() {
let b=MyBox::new(12);
assert_eq!(*b,12);
}
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(value: T) -> Self {
MyBox(value)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
當調用*b時,本質上是*(b.deref()),這樣&引用和Deref引用的形式就統一了。
函數和方法的隱式解引用強制多態
假設我們有一個函數,它需要一個&str類型的參數。
fn print_name(name: &str){
println!("name:{}",name);
}
改變main中的代碼
let string = MyBox::new(String::from("My Box"));
print_name(&(*string))
*string獲得 String 類型,調用String類型的&來獲取&str類型(可以查看標准庫,實現了AsRef<str> trait)。
如果沒有隱式解引用強制多態,我們需要這么寫代碼。很明顯,這種寫法太啰嗦了,本來rust的符號一大堆,現在這樣子根本無法忍受。
我們稍微更改一下
print_name(&string);
其他代碼不變,把參數改為&string,明顯,這是獲取一個&MyBox<String>的對象,然后rust會自動調用deref()轉為&String,&String調用deref()轉為&str(String也實現了Deref trait),這樣子就與函數簽名匹配了,編譯通過。
也就是,rust會隱式解引用來匹配所聲明變量(參數)的類型。
let a:&str = &string;
println!("{}", a);
這樣的代碼也是有效的。
Rust 在發現類型和 trait 實現滿足三種情況時會進行解引用強制多態:
- 當
T: Deref<Target=U>時從&T到&U。 - 當
T: DerefMut<Target=U>時從&mut T到&mut U。 - 當
T: Deref<Target=U>時從&mut T到&U。
Deref trait 解引用為 &T ,即不可變引用, 而 DerefMut trait 解引用為 &mut T,是可變引用。
3.Drop trait
Drop是專門用來進行一些資源釋放的操作,比如IO操作,當對象離開作用域時,會自動調用 Drop 的 drop 方法來釋放資源。
use std::fmt::Display;
fn main() {
let mb=MyBox::new(2);
println!("the end");
}
struct MyBox<T:Display>(T);
impl<T:Display> MyBox<T> {
fn new(value: T) -> Self {
MyBox(value)
}
}
impl<T:Display> Drop for MyBox<T>{
fn drop(&mut self) {
println!("the data is {}",self.0);
}
}
泛型約束T:Display是為了讓它能夠被打印。這段代碼最終輸出
the end
the data is 2
提前釋放資源
有時候,我們需要提前釋放資源。比如寫入文件完成后,需要立馬 close 。但是,Drop::drop 是不允許手動調用的,這時,我們需要 std::mem::drop 函數來釋放。
修改main函數
fn main() {
let mb=MyBox::new(2);
drop(mb);
println!("the end");
}
輸出結果
the data is 2
the end
這表明,Drop::drop被提前調用了,並且只調用了一次。
注意 std::mem::drop 的參數是 move 語義,也就是說,在調用 std::mem::drop 之后,mb 已經被移動,不能夠再用了。
那么這個函數是怎么實現的呢?我們跳轉到 drop 的實現
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn drop<T>(_x: T) { }
對,它的函數體是空的!!!
其實不難理解,因為 mb 被移動到 drop 中了,在 drop 函數結束后,mb.drop() 就會被調用 ,這樣就實現了資源的提前釋放。
