rust学习Cell、RefCell、OnceCell

背景

Rust 内存安全基于以下规则:给定一个对象 T,它只能具有以下之一:

  • 对对象有多个不可变引用 (&T)(也称为别名 aliasing)
  • 对对象有一个可变引用 (&mut T)(也称为可变性 mutability)

这是由 Rust 编译器强制执行的。然而,在某些情况下,该规则不够灵活(this rule is not flexible)。有时需要对一个对象有多个引用并对其进行修改(it is required to have multiple references to an object and yet mutate it)。

可共享的可变容器的存在是为了以受控的方式允许可变性(permit mutability in a controlled manner),即使存在别名(in the presence of aliasing)也是如此。 Cell、RefCell 和 OnceCell 允许以单线程方式执行此操作,它们不实现 Sync。(如果需要在多个线程之间进行别名和突变 do aliasing and mutation among multiple threads,Mutex、RwLock、OnceLock 或原子类型是执行此操作的正确数据结构)。

介绍

Cell、RefCell 和 OnceCell 类型的值可以通过共享引用(即公共 &T 类型)进行mutate,而大多数 Rust 类型只能通过唯一 (&mut T) 引用(unique reference)进行mutate。
这些cell类型提供了“内部可变性,interior mutability”(通过 &T 可变),与表现出“继承可变性”(仅通过 &mut T 可变)的典型 Rust 类型形成鲜明对比。

Cell类型分为三种类型:Cell、RefCell 和 OnceCell。每个都提供了一种不同的方式来提供安全的内部可变性(providing safe interior mutability)。

Cell

Cell 通过将值移入和移出cell来实现内部可变性(interior mutability)。也就是说,永远无法获得内部值的 &mut T,并且如果不将其替换为其他值,则无法直接获得该值本身。
这两条规则都确保永远不会有多个引用指向内部值。
该类型提供了以下方法:

  • 对于实现 Copy 的类型, get 方法通过复制当前内部值来检索它
  • 对于实现 Default 的类型, take 方法用 Default::default() 替换当前内部值并返回替换的值

所有的类型拥有:

  • replace:替换当前内部值并返回替换后的值
  • into_inner:此方法消耗 Cell 并返回内部值
  • set:此方法替换内部值,删除替换的值

Cell 通常用于更简单的类型,其中复制或移动值不太占用资源(例如数字),并且在可能的情况下通常应优先于其他cell类型。对于较大的no-copy类型,RefCell 提供了一些优势。

RefCell

RefCell 使用 Rust 的生命周期来实现“动态借用 dynamic borrowing”,这是一个可以声明对内部值的临时、独占、可变访问的过程(claim temporary, exclusive, mutable access)。 RefCell 的借用是在运行时跟踪的,这与 Rust 的引用类型(native reference)不同,后者在编译时完全静态跟踪。

对 RefCell 内部值的不可变引用 (&T) 可以通过 Borrow 获得,可变借用(mutable borrow) (&mut T)可以通过 Borrow_mut 获得。
当这些函数被调用时,它们首先验证 Rust 的借用规则是否得到满足:允许任意数量的不可变借用或允许单个可变借用,但决不能两者兼而有之。如果尝试借用违反这些规则,线程将出现panic。

RefCell 对应的 Sync 版本是 RwLock。

OnceCell

OnceCell 有点像 Cell 和 RefCell 的混合体(somewhat of a hybrid of Cell and RefCell),适用于通常只需要设置一次的值。这意味着无需移动或复制内部值(与 Cell 不同),也无需运行时检查(与 RefCell 不同)即可获取引用 &T。
但是,它的值一旦设置就无法更新,除非有对 OnceCell 的可变引用。
OnceCell提供了以下方法:

  • get:获取内部值的引用
  • set:如果未设置则设置内部值(返回Result)
  • get_or_init:返回内部值,如果需要则初始化它
  • get_mut:提供对内部值的可变引用,仅当有对单元格本身的可变引用时才可用
    OnceCell 对应的Sync版本是 OnceLock

何时选择 interior mutability

更常见的继承可变性(必须具有唯一的访问权限才能改变值 have unique access to mutate a value)是关键语言元素之一,它使 Rust 能够对指针别名进行强有力的推理reason strongly about pointer aliasing,静态地防止崩溃错误。

因此,继承的可变性是首选,而内部可变性是最后的手段(inherited mutability is preferred, and interior mutability is something of a last resort)。

由于cell类型能够在不允许突变的情况下发生突变(Since cell types enable mutation where it would otherwise be disallowed though),因此有时内部可变性可能是合适的(there are occasions when interior mutability might be appropriate),甚至必须使用,

例如

  • 在不可变事物的“内部”引入可变性 Introducing mutability ‘inside’ of something immutable
  • 逻辑上不可变方法的实现细节 Implementation details of logically-immutable methods
  • 改变Clone的实现 Mutating implementations of Clone

在不可变事物的“内部”引入可变性

许多共享智能指针类型(包括 Rc 和 Arc)提供了可以在多方之间(multiple parties)克隆和共享的容器。由于所包含的值可能是多重别名(multiply-aliased),因此只能使用 & 借用它们,而不能使用 &mut。如果没有cell,就根本不可能改变这些智能指针内部的数据。
将 RefCell 放入共享指针类型中以重新引入可变性( reintroduce mutability)是很常见的

use std::cell::{
   RefCell, RefMut};
use std::collections::HashMap;
use std::rc::Rc;

fn main() 

你可能感兴趣的:(rust,rust,学习,开发语言)