Box

使用场景

  1. 链表类型,需要在数据域中放一个Box类型指针,例如Cons list
  2. 想把数据存在堆上,这样能使用所有权,避免在栈上拷贝数据
  3. 用于**trait object**,即Box<dny TraitType>的形式

Cons list实例

  1. #[derive(Debug)]
  2. enum List{
  3. Cons(i32,Box<List>),
  4. Nil
  5. }
  6. use self::List::{Cons,Nil};
  7. fn main() {
  8. let a = Box::new(5);
  9. println!("{}",a);
  10. let b = Cons(1,Box::new(
  11. Cons(2,Box::new(
  12. Cons(3,Box::new(Nil))))));
  13. println!("{:?}",b);
  14. // Cons(1, Cons(2, Cons(3, Nil)))
  15. }

trpl15-02.svg

Drop trait

概念

在值要离开作用域时执行一些代码

Drop几乎总是用于实现智能指针

离开作用域时,变量以被创建时相反的顺序被丢弃

实例

为结构体实现Drop trait
使用std::mem::drop主动丢弃变量

  1. struct CustomSmartPointer {
  2. data: String,
  3. }
  4. impl Drop for CustomSmartPointer {
  5. fn drop(&mut self) {
  6. println!("Dropping CustomSmartPointer with data `{}`!", self.data);
  7. }
  8. }
  9. fn main() {
  10. let c = CustomSmartPointer { data: String::from("some data") };
  11. println!("CustomSmartPointer created.");
  12. drop(c);
  13. println!("CustomSmartPointer dropped before the end of main.");
  14. }

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
trpl15-03.svg

  1. #[derive(Debug)]
  2. enum List {
  3. Cons(i32, Rc<List>),
  4. Nil,
  5. }
  6. use crate::List::{Cons, Nil};
  7. use std::rc::Rc;
  8. fn main() {
  9. let a = Rc::new(Cons(5,Rc::new(Cons(10,Rc::new(Nil)))));
  10. println!("{:?}",a);
  11. // Cons(5, Cons(10, Nil))
  12. let b = Cons(3,Rc::clone(&a));
  13. let c = Cons(4,Rc::clone(&a));
  14. println!("{:?}",b);
  15. // Cons(3, Cons(5, Cons(10, Nil)))
  16. println!("{:?}",c);
  17. // Cons(4, Cons(5, Cons(10, Nil)))
  18. }

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()方法,指明如何发消息:

  1. pub trait Messenger {
  2. fn send(&self, msg: &str);
  3. }

LimitTracker结构体,用来对比传入的value参数与max的差距,并调用send()方法发送消息:

  1. struct LimitTracker<'a,T:Messenger> {
  2. messenger : &'a T,
  3. value:i32,
  4. max:i32
  5. }
  6. impl<'a,T> LimitTracker<'a, T>where T: Messenger {
  7. fn new(messenger: &T , max:i32) -> LimitTracker<T> {
  8. LimitTracker {
  9. messenger,
  10. value:0,
  11. max
  12. }
  13. }
  14. fn set_value(&mut self, value:i32){
  15. self.value = value;
  16. let percent = self.value as f64 / self.max as f64;
  17. if percent >= 1.0{
  18. self.messenger.send("more than 100%")
  19. }else if percent > 0.5 {
  20. self.messenger.send("use 50%")
  21. }else if percent > 0.2 {
  22. self.messenger.send("use 20%")
  23. }
  24. }
  25. }

测试模块

实现Messenger trait的结构体,使用Vec存储所有消息:

  1. use super::*;
  2. use std::cell::RefCell;
  3. struct MockMessenger {
  4. msg_list:RefCell<Vec<String>>
  5. }
  6. impl MockMessenger {
  7. fn new() -> Self{
  8. MockMessenger{
  9. msg_list:RefCell::new(Vec::new()),
  10. }
  11. }
  12. }
  13. impl Messenger for MockMessenger {
  14. fn send(&self, m:&str){
  15. self.msg_list.borrow_mut().push(String::from(m));
  16. }
  17. }

LimitTracker实例,给定不同的value,发送不同的消息:

  1. #[test]
  2. fn test_limit_tracker () {
  3. let messenger = MockMessenger::new();
  4. let mut tracker = LimitTracker::new(&messenger,100);
  5. tracker.set_value(110);
  6. tracker.set_value(70);
  7. tracker.set_value(30);
  8. println!("{:?}",messenger.msg_list);
  9. }