智能指针通常使用struct实现,
并实现Deref和Drop这两个trait
Deref trait:允许智能指针struct 的实例像引用一样使用
Drop triat: 允许你自定义当智能指针实例走出作用域时的代码
标准库中常见的智能指针
Box:在heap内存上分配值
Rc: 启用多重所有权的引用技术类型
Ref RefMut 通过RefCall 访问:在运行时而不是编译时强制借用规则的类型
他是最简单的智能指针
let b = Box::new(5);
println!("b = {}",b);
rust 编译时需要知道一个类型所占的空间大小
但是递归类型的大小在编译时无法确认大小
使用Box可以解决,Box是指针,大小确认
Deref 解引用,我们可以自定义解引用运算符*的行为
通过Deref,智能指针可以像常规引用一样来处理
解引用运算符
let x = 5;
let y = &x;
assert_eq!(5,x);
assert_eq!(5,*y);
使用box
let y = Box::new(5);
assert_eq!(5,*y);
struct Mypointer<T>(T); //结构体元组,只有一个成员
//元组结构体相当于没有成员名字的结构体,通过索引访问
impl<T> Mypointer<T> {
fn new(x : T) -> Mypointer<T> {
Mypointer(x)
}
}
//要让其成为指针,需要实现Deref方法
impl<T> Deref for Mypointer<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
let y = Mypointer::new(5);
assert_eq!(5,*y);
当传入类型与函数接收类型不匹配时,如果参数实现了Deref trait,则编译器会自动调用Deref方法,对参数类型进行匹配;
例子:
fn hello(name : & str) {
println!("hello,{}",name);
}
hello("Rust");
let m = Mypointer::new(String::from("Rust"));
//原始类型为 &mypointer
// deref &string
// deref &str
hello(&m);
实现后,可以自定义值离开作用域时发生的动作
要求实现drop方法
在变量离开作用域时,会自动调用drop方法
例子:
impl<T> Drop for Mypointer<T> {
fn drop(&mut self) {
println!("run drop function----")
}
}
不能手动调用.drop()方法
但是可以调用drop(变量)函数 进行手动注销
有时,一个值会有多个所有者
为了支持多重所有权,引入 Rc
Rc只能用于单线程场景
方法:
Rc::clone(&a)函数:增加引用计数
Rc::strong_count(&a): 获得引用计数
例子:
enum Node2 {
Next2(i32 ,Rc<Node2> ),
Nul
}
use self::Node2::Next2;
use self::Node2::Nul;
.... main.....
let a = Rc::new( Next2(5, Rc::new( Nul ) ));
println!("a value is {}",Rc::strong_count(&a));
let b = Rc::new( Next2(
12, Rc::clone(&a)
)
);
println!("after b :a value is {}",Rc::strong_count(&a));
let c = Rc::new(
Next2( 11, Rc::clone(&a) )
);
println!("after c: a value is {}",Rc::strong_count(&a));
{
let d = Rc::new(
Next2( 15, Rc::clone(&a) )
);
println!("after d :a value is {}",Rc::strong_count(&a));
}
println!("end : a value is {}",Rc::strong_count(&a));
....end....
通过不可变的引用,使你可以在程序不同部分之间共享只读数据
与clone()相比,属于浅拷贝,执行速度快
内部可变性:
允许在只持有不可变引用的前提下对数据进行修改
RefCell 在运行时检查所有权规则
只能用于单线程的代码
Box | Rc | RefCell | |
---|---|---|---|
同一数据所有者 | 一个 | 多个 | 一个 |
可变性、借用检查 | 可变、不可变借用(编译时检查) | 不可变借用(编译时检查) | 可变、不可变借用(运行时检查) |
正常情况下无法借用一个不可变的可变借用
let a = 10;
let b = &mut a;//错误
Refcall 的 .borrow_mut()方法:修改不可变引用的值