本文概述
内部可变性模式是一种在我们具有不可变参考的情况下用于对参考进行变异的模式。 RefCell <T>可用于实现内部可变性。
要记住的要点:
- RefCell <T>表示对其拥有的数据的单一所有权。
- 如果我们使用RefCell <T>, 则在运行时强制执行不变式。
- RefCell <T>主要用于单线程方案, 如果在多线程情况下使用, 将给出错误。
- RefCell <T>在运行时检查可变借位。因此, 可以说即使RefCell <T>值是不可变的, 我们也可以对其进行突变。
内部可变性
根据借款规则, 如果我们拥有不变的价值, 那么我们就不能可变地借款。
让我们看一个简单的例子:
fn main()
{
let a = 15;
let b = &mut a;
}
输出
在上面的示例中, 我们看到不可变值不能可变地借用。但是, RefCell是实现内部可变性的一种方法。
使用RefCell <T>在运行时跟踪借入
RefCell <T>由两个在运行时跟踪借用的方法组成:
- 借款():借款()方法返回类型为Ref <T>的智能指针。
- 借用_mut():借用_mut()方法返回类型为RefMut << T>的智能指针。
注意:Ref <T>和RefMut <T>类型都实现Deref特征。因此, 可以将它们视为常规参考。
一些要点:
- RefCell <T>记录当前有多少Ref <T>和Refmut <T>智能指针处于活动状态。
- 每当调用rowe()方法时, RefCell <T>都会增加活动的不可变借位的数量。当Rc <T>超出范围时, RefCell <T>会将计数减少一。
- RefCell <T>使我们有许多不可变的借项, 但一次只有一个可变的借项, 就像编译时的借用规则一样。如果我们违反此规则, 则RefCell <T>将在运行时惊慌。
借款()方法
借款()方法借用不可变值。可以同时进行多次不可变借用。
句法:
pub fn borrow(&self) -> Ref<T>
让我们看一个发生多个不可变借位的简单示例:
use std::cell::RefCell;
fn main()
{
let a = RefCell::new(15);
let b = a.borrow();
let c = a.borrow();
println!("Value of b is : {}", b);
println!("Value of c is : {}", c);
}
输出
让我们看一个紧急情况的简单例子:
use std::cell::RefCell;
fn main()
{
let a = RefCell::new(10);
let b = a.borrow();
let c = a.borrow_mut(); // cause panic.
println!("Value of b is : {}", b);
println!("Value of c is : {}", c);
}
输出
在上面的示例中, 程序在运行时由于不可变借项和可变借项而出现紧急情况, 不能同时发生。
loan_mut()方法
借款的方法mutable_value。可变借用只能发生一次。
句法:
pub fn borrow_mut(&self) -> RefMut<T>;
让我们看一个简单的例子:
use std::cell::RefCell;
fn main()
{
let a = RefCell::new(15);
let b = a.borrow_mut();
println!("Now, value of b is {}", b);
}
让我们看一个紧急情况的简单例子:
use std::cell::RefCell;
fn main()
{
let a = RefCell::new(15);
let b = a.borrow_mut();
let c = a.borrow_mut();
}
输出
在上面的示例中, 可变借贷发生了两次。因此, 程序在运行时出现紧急情况, 并引发错误”已借用:BorrowMutError”。
通过组合Rc <T>和RefCell <T>的可变数据的多个所有者
我们可以结合使用Rc <T>和RefCell <T>, 以便我们可以拥有可变数据的多个所有者。 Rc <T>允许你有一个数据的多个所有者, 但它仅提供对数据的不变访问。 RefCell <T>使你可以对数据进行突变。因此, 可以说Rc <T>和RefCell <T>的组合提供了具有可变数据的多个所有者的灵活性。
让我们看一个简单的例子:
#[derive(Debug)]
enum List
{
Cons(Rc<RefCell<String>>, Rc<List>), Nil, }
use List:: {Cons, Nil};
use std::rc::Rc;
use std::cell::RefCell;
fn main()
{
let val = Rc::new(RefCell::new(String::from("java")));
let a = Rc::new(Cons(Rc::clone(&val), Rc::new(Nil)));
let b = Cons(Rc::new(RefCell::new(String::from("C"))), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(String::from("C++"))), Rc::clone(&a));
*val.borrow_mut() = String::from("C# language");
println!("value of a is : {:?}", a);
println!("value of b is : {:?}", b);
println!("value of c is : {:?}", c);
}
输出
在上面的示例中, 我们创建了一个变量” val”, 并将值” java”存储到变量” val”中。然后, 我们创建列表” a”, 并克隆” val”变量, 以便变量” a”和” val”都具有” java”值的所有权, 而不是将所有权从” val”转移到” a”变量。创建” a”列表后, 我们创建” b”和” c”列表并克隆” a”列表。创建列表之后, 通过使用rowe_mut()方法, 将” val”变量的值替换为” C#”语言”。