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()
就会被调用 ,这样就实现了资源的提前释放。