Module std::cell 1.0.0[−][src]
Expand description
可共享的可变容器。
Rust 内存安全基于以下规则: 给定对象 T
,则只能具有以下之一:
- 对对象具有多个不可变引用 (
&T
) (也称为别名)。 - 对对象有一个可变引用 (
&mut T
) (也称为 可变性)。
这由 Rust 编译器强制执行。但是,在某些情况下,此规则不够灵活。有时需要对一个对象进行多次引用,然后对其进行可变的。
存在共享的可变容器以允许以受控的方式进行可变性,即使在出现混叠的情况下也是如此。Cell<T>
和 RefCell<T>
都允许以单线程方式执行此操作。
但是,Cell<T>
和 RefCell<T>
都不是线程安全的 (它们不实现 Sync
)。
如果需要在多个线程之间进行别名和可变的,则可以使用 Mutex<T>
,RwLock<T>
或 atomic
类型。
Cell<T>
和 RefCell<T>
类型的值可以通过共享引用 (例如
常见的 &T
类型),而大多数 Rust 类型只能通过唯一的 (&mut T
) 引用进行可变的。
我们说 Cell<T>
和 RefCell<T>
提供了内部可变性,而典型的 Rust 类型却表现出继承的可变性。
Cell 类型有两种: Cell<T>
和 RefCell<T>
。Cell<T>
通过将值移入和移出 Cell<T>
来实现内部可变性。
要使用 quot 代替值,必须使用 RefCell<T>
类型,在可变之前获取一个写锁。Cell<T>
提供了检索和更改当前内部值的方法:
- 对于实现
Copy
的类型,get
方法检索当前内部值。 - 对于实现
Default
的类型,take
方法将当前内部值替换为Default::default()
,然后返回替换后的值。 - 对于所有类型,
replace
方法将替换当前内部值并返回替换后的值,而into_inner
方法将使用Cell<T>
并返回内部值。 此外,set
方法替换内部值,丢弃替换后的值。
RefCell<T>
使用 Rust 的生命周期实现动态借用,这一过程使人们可以要求临时,排他,可变地访问内部值。
借用 forRefCell<T>s 是在
运行时 被跟踪的,这与 Rust 的原生引用类型不同,后者在编译时是完全静态跟踪的。 由于
RefCell
何时选择内部可变性
更常见的继承的可变性 (其中必须具有对值的唯一访问权) 是使 Rust 能够强烈考虑指针别名的关键语言元素之一,从而可以静态地防止崩溃错误。 因此,首选继承的可变性,而内部可变性则是不得已而为之。 由于细胞类型可以实现原本不允许的可变的,因此有时可能需要内部可变性,甚至必须 使用
- 介绍事物的可变性 ‘inside’
- 逻辑不可变方法的实现细节。
- 更改
Clone
的实现。
介绍事物的可变性 ‘inside’
许多共享的智能指针类型,包括 Rc<T>
和 Arc<T>
,都提供了可以在多方之间克隆和共享的容器。
由于所包含的值可能会被乘以别名,因此只能通过 &
借用,而不能通过 &mut
借用。
没有单元,根本不可能在这些智能指针中对数据进行可变的。
然后将 RefCell<T>
放在共享指针类型中以重新引入可变性是很常见的:
use std::cell::{RefCell, RefMut}; use std::collections::HashMap; use std::rc::Rc; fn main() { let shared_map: Rc<RefCell<_>> = Rc::new(RefCell::new(HashMap::new())); // 创建一个新块以限制动态借用的作用域 { let mut map: RefMut<_> = shared_map.borrow_mut(); map.insert("africa", 92388); map.insert("kyoto", 11837); map.insert("piccadilly", 11826); map.insert("marbles", 38); } // 请注意,如果我们没有让缓存的上一次借用离开作用域,那么后续的借用将导致动态线程 panic。 // // 这是使用 `RefCell` 的主要危险。 let total: i32 = shared_map.borrow().values().sum(); println!("{}", total); }Run
请注意,此示例使用 Rc<T>
而不是 Arc<T>
。RefCell<T>
s 用于单线程方案。如果在多线程情况下需要共享可变性,请考虑使用 RwLock<T>
或 Mutex<T>
。
逻辑不可变方法的实现细节
有时可能希望不要在 API 中公开 “under the hood” 发生了可变的。
这可能是因为逻辑上该操作是不可变的,但是例如,缓存会强制实现执行可变的; 或因为必须使用可变来实现最初定义为采用 &self
的 trait 方法。
use std::cell::RefCell; struct Graph { edges: Vec<(i32, i32)>, span_tree_cache: RefCell<Option<Vec<(i32, i32)>>> } impl Graph { fn minimum_spanning_tree(&self) -> Vec<(i32, i32)> { self.span_tree_cache.borrow_mut() .get_or_insert_with(|| self.calc_span_tree()) .clone() } fn calc_span_tree(&self) -> Vec<(i32, i32)> { // 昂贵的计算在这里 vec![] } }Run
改变 Clone
的实现
这只是前一种情况的一种特殊情况 - 但很常见 - 在看起来不可变的操作中隐藏了可变性。
预期 clone
方法不会更改源值,并声明采用 &self
,而不是 &mut self
。
因此,在 clone
方法中发生的任何可变的都必须使用细胞类型。
例如,Rc<T>
在 Cell<T>
内保持其引用计数。
use std::cell::Cell; use std::ptr::NonNull; use std::process::abort; use std::marker::PhantomData; struct Rc<T: ?Sized> { ptr: NonNull<RcBox<T>>, phantom: PhantomData<RcBox<T>>, } struct RcBox<T: ?Sized> { strong: Cell<usize>, refcount: Cell<usize>, value: T, } impl<T: ?Sized> Clone for Rc<T> { fn clone(&self) -> Rc<T> { self.inc_strong(); Rc { ptr: self.ptr, phantom: PhantomData, } } } trait RcBoxPtr<T: ?Sized> { fn inner(&self) -> &RcBox<T>; fn strong(&self) -> usize { self.inner().strong.get() } fn inc_strong(&self) { self.inner() .strong .set(self.strong() .checked_add(1) .unwrap_or_else(|| abort() )); } } impl<T: ?Sized> RcBoxPtr<T> for Rc<T> { fn inner(&self) -> &RcBox<T> { unsafe { self.ptr.as_ref() } } }Run
Structs
BorrowError |
|
BorrowMutError |
|
Cell | 可变的内存位置。 |
Ref | 在 |
RefCell | 具有动态检查借用规则的可变内存位置 |
RefMut | 从 |
UnsafeCell | Rust 中内部可变性的核心原语。 |