Box
使用场景
- 链表类型,需要在数据域中放一个
Box
类型指针,例如Cons list
- 想把数据存在堆上,这样能使用所有权,避免在栈上拷贝数据
- 用于
**trait object**
,即Box<dny TraitType>
的形式
Cons list实例
#[derive(Debug)]
enum List{
Cons(i32,Box<List>),
Nil
}
use self::List::{Cons,Nil};
fn main() {
let a = Box::new(5);
println!("{}",a);
let b = Cons(1,Box::new(
Cons(2,Box::new(
Cons(3,Box::new(Nil))))));
println!("{:?}",b);
// Cons(1, Cons(2, Cons(3, Nil)))
}
Drop trait
概念
在值要离开作用域时执行一些代码
Drop
几乎总是用于实现智能指针
离开作用域时,变量以被创建时相反的顺序被丢弃
实例
为结构体实现Drop trait
使用std::mem::drop
主动丢弃变量
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer { data: String::from("some data") };
println!("CustomSmartPointer created.");
drop(c);
println!("CustomSmartPointer dropped before the end of main.");
}
Rc
概念
Rc<T>
叫做引用计数,把数据包裹在其中并存在堆上,允许栈中有多个变量指向它
用来实现变量的多所有权
但是仅适用于单线程
使用方式
创建Rc<T>
类型的所有权变量let a: Rc<i32> = Rc::new(5);
通过Rc::clone
创建第二个所有权变量let b: Rc<i32> = Rc::clone(&a);
使用Rc::strong_count
查看引用计数println!("{}",Rc::strong_count(&a));
实例
实现下图的Cons list
#[derive(Debug)]
enum List {
Cons(i32, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
fn main() {
let a = Rc::new(Cons(5,Rc::new(Cons(10,Rc::new(Nil)))));
println!("{:?}",a);
// Cons(5, Cons(10, Nil))
let b = Cons(3,Rc::clone(&a));
let c = Cons(4,Rc::clone(&a));
println!("{:?}",b);
// Cons(3, Cons(5, Cons(10, Nil)))
println!("{:?}",c);
// Cons(4, Cons(5, Cons(10, Nil)))
}
RefCell
内部可变性
内部可变性是一种设计模式,是指可以通过不可变的引用(所有权/不可变引用)拿到可变引用,Rust
中使用RefCell<T>
实现
RefCell<T>
内部使用的是unsafe
代码,但是提供了安全的API
:
- 使用
borrow_mut()
拿到的是可变引用,类型是RefMut<T>
智能指针 - 使用
borrow()
拿到的是不可变引用,类型是Ref<T>
智能指针
RefCell<T>
仅适用于单线程场景
运行时检查
RefCell<T>
记录当前有多少个活动的Ref<T>
和RefMut<T>
智能指针。每次调用borrow()
,RefCell<T>
将活动的不可变借用计数加一。当 Ref<T>
值离开作用域时,不可变借用计数减一。
就像编译时借用规则一样,RefCell<T>
在任何时候只允许有多个不可变借用或一个可变借用,只不过是在运行时才检查这个规则
与Box、Rc对比
Box |
Rc |
RefCell |
|
---|---|---|---|
所有权 | 单一 | 多个 | 单一 |
借用规则 | 多个不可变借用或 一个可变借用 |
只能有多个不可变借用 | 多个不可变借用或 一个可变借用 |
检查时机 | 编译时 | 编译时 | 运行时 |
与Rc结合使用
Rc<T>
能有多所有权,但是每个所有权拿到的都是不可变引用,无法更改其内部数据
可以将RefCell<T>
包在Rc<T>
当中:Rc<RefCell<T>>
,这时就可以先获取数据的可变引用,然后对数据进行变更
示例
一个用于发消息的库
消息trait
,包括一个send()
方法,指明如何发消息:
pub trait Messenger {
fn send(&self, msg: &str);
}
LimitTracker
结构体,用来对比传入的value
参数与max
的差距,并调用send()
方法发送消息:
struct LimitTracker<'a,T:Messenger> {
messenger : &'a T,
value:i32,
max:i32
}
impl<'a,T> LimitTracker<'a, T>where T: Messenger {
fn new(messenger: &T , max:i32) -> LimitTracker<T> {
LimitTracker {
messenger,
value:0,
max
}
}
fn set_value(&mut self, value:i32){
self.value = value;
let percent = self.value as f64 / self.max as f64;
if percent >= 1.0{
self.messenger.send("more than 100%")
}else if percent > 0.5 {
self.messenger.send("use 50%")
}else if percent > 0.2 {
self.messenger.send("use 20%")
}
}
}
测试模块
实现Messenger trait
的结构体,使用Vec
存储所有消息:
use super::*;
use std::cell::RefCell;
struct MockMessenger {
msg_list:RefCell<Vec<String>>
}
impl MockMessenger {
fn new() -> Self{
MockMessenger{
msg_list:RefCell::new(Vec::new()),
}
}
}
impl Messenger for MockMessenger {
fn send(&self, m:&str){
self.msg_list.borrow_mut().push(String::from(m));
}
}
LimitTracker
实例,给定不同的value
,发送不同的消息:
#[test]
fn test_limit_tracker () {
let messenger = MockMessenger::new();
let mut tracker = LimitTracker::new(&messenger,100);
tracker.set_value(110);
tracker.set_value(70);
tracker.set_value(30);
println!("{:?}",messenger.msg_list);
}