Rust-智能指针:RefCell<T>和内部可变性模式

  内部可变性 (Interior mutability) 是Rust中的一个设计模式,它允许你即使在有不可变引用时也可以改变数据,这通常是借用规则不允许的。为了改变数据,该模式在数据结构中使用 unsafe 代码来模糊Rust通常的可变性和借用规则。

通过RefCell<T>在运行时检查借用规则

不同于Rc<T>,RefCell<T>代表其数据的唯一的所有权。那么是什么让RefCell<T>不同于像Box<T>这样的类型呢?我们回想一下借用规则:

  1. 在任意给定时该,只能拥有一个可变引用或任意数量的不可变引用之一(而不是两者)。
  2. 引用必须总是有效的。

对于引用和Box<T>,借用规则的不可变性用于编译时。对于RefCell<T>,这些不可变性作用于运行时。对于引用,如果违反这些规则,会得到一个编译错误。而对于RefCell<T>,如果违反这些规则程序会panic并退出。

因为一些分析是不可能的,如果Rust编译器不能通过所有权规则编译,它可能会拒绝一个正确的程序。如果Rust接受不正确的程序,虽然会给程序带来不便,但不会带来灾难。RefCell<T>正是用于当你确信代码遵守借用规则,而编译器不能理解和确定的时候。

类似于Rc<T>,RefCell<T>只能用于单线程场景。

如下为选择Box<T>,Rc<T>或RefCell<T>的理由:

  • Rc<T>允许相同数据有多个所有者;Box<T>和RefCell<T>有单一所有者。
  • Box<T>允许在编译时执行不可变或可变借用检查;Rc<T>仅允许在编译时执行不可变借用检查;RefCell<T>允许在运行时执行不可变或可变借用检查。
  • 因为RefCell<T>允许在运行时执行可变借用检查,所以我们可以在即便RefCell<T>自身是不可变的情况下修改其内部的值。

在不可变值内部改变值就是内部可变性模式。让我们看看何时内部可变性是有用的。

内部可变性:不可变值的可变借用

借用规则的一个推论是当有一个不可变值时,不能可变地借用它。如下代码不能编译:

    let x = 5;
    let y = &mut x;

有以下错误:

error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
  --> src/main.rs:41:13
   |
40 |     let x = 5;
   |         - help: consider changing this to be mutable: `mut x`
41 |     let y = &mut x;
   |             ^^^^^^ cannot borrow as mutable

然而,特定情况下,令一个值在其方法内部能够修改自身,而在其它代码中仍视为不可变,是很有用的。值方法外部的代码就不能修改其值。

RefCell<T>是一个获得内部可变性的方法。RefCell<T>并没有完全绕开借用规则,编译器中的借用检查器允许内部可变性并相应地在运行时检查借用规则。如违反了这些规则,会出现panic而不是编译错误。

内部可变性的用例:mock对象

测试替身(test double)是一个通用编程概念,它代表一个在测试中替代某个类型的类型。mock对象是特定类型的测试替身,它们记录测试过程中发生了什么以便可以断言操作是正确的。

深入学习

原文地址:https://www.cnblogs.com/johnnyzhao/p/15356157.html