Rust std::cell
std::cell
今天病终于好了,不用上腹下泄了,来写点水文庆祝一下🎉
UnsafeCell
UnsafeCell 的动机在 F001 大佬的 这篇文章 里面提及了,提一下作者写的书还是很不错的,对我帮助很大。
UnsafeCell 结构如下所示:
1 |
|
注意一下 marker:
1 |
|
比较重要的方法是 get:
1 | impl<T: ?Sized> UnsafeCell<T> { |
repr(transparent) 资料在:
- https://github.com/rust-lang/rust/issues/43036
- https://github.com/rust-lang/rfcs/pull/1758
- https://github.com/rust-lang/rfcs/blob/master/text/1758-repr-transparent.md
On some ABIs, structures with one field aren’t handled the same way as values of the same type as the single field. For example on ARM64, functions returning a structure with a single
f64field return nothing and take a pointer to be filled with the return value, whereas functions returning af64return the floating-point number directly.This means that if someone wants to wrap a
f64value in a struct tuple wrapper and use that wrapper as the return type of a FFI function that actually returns a baref64, the calls to this function will be compiled incorrectly by Rust and the execution of the program will segfault.This also means that
UnsafeCellcannot be soundly used in place of a bareTin FFI context, which might be necessary to signal to the Rust side of things that thisTvalue may unexpectedly be mutated.
std::cell::Cell
Cellimplements interior mutability by moving values in and out of theCell. To use references instead of values, one must use theRefCelltype, acquiring a write lock before mutating.Cellprovides methods to retrieve and change the current interior value:
- For types that implement
Copy, thegetmethod retrieves the current interior value.- For types that implement
Default, thetakemethod replaces the current interior value withDefault::default()and returns the replaced value.- For all types, the
replacemethod replaces the current interior value and returns the replaced value and theinto_innermethod consumes theCelland returns the interior value. Additionally, thesetmethod replaces the interior value, dropping the replaced value.
对于 Cell 而言,不一定完全需要你实现 Copy 才能用,get 需要你实现 Copy, set 会移除原先内部的
1 |
|
对应的几个 marker
1 |
|
可以看到,从结构设计来看, Cell 相对来说额外开销小一些。
Get/Set:
1 | impl<T:Copy> Cell<T> { |
这个 get 需要实现 copy trait, 拿到 UnsafeCell 内部的数值。然后把这个旧值 drop 掉。
1 | impl<T> Cell<T> { |
看来我们需要看看 replace:
1 |
|
mem::replace 的文档如下:
1 | pub fn replace<T>(dest: &mut T, src: T) -> T |
Moves
srcinto the referenceddest, returning the previousdestvalue.Neither value is dropped.
返回 UnsafeCell 保存的值,同时写入 UnsafeCell.
std::cell::RefCell
RefCelluses Rust’s lifetimes to implement ‘dynamic borrowing’, a process whereby one can claim temporary, exclusive, mutable access to the inner value. Borrows forRefCells are tracked ‘at runtime’, unlike Rust’s native reference types which are entirely tracked statically, at compile time. BecauseRefCellborrows are dynamic it is possible to attempt to borrow a value that is already mutably borrowed; when this happens it results in thread panic.
1 | /// A mutable memory location with dynamically checked borrow rules |
它相对 Cell 多维护了一个 borrow 字段:
1 | // Positive values represent the number of `Ref` active. Negative values |
可以看到:
UNUSED表示这个RefCell并没有任何借用- Positive value: 表示
Ref, 这里可以推测是&T模式的借用存在 - Nagative value: 表示
RefMut, 这里可以推测是&mut T模式的借用存在
让我们看看 try_borrow_mut:
1 | /// Mutably borrows the wrapped value, returning an error if the value is currently borrowed. |
这里有相当重要的 BorrowRefMut:
1 | struct BorrowRefMut<'b> { |
1 |
|
返回的是一个 RefMut, 带有值和 BorrowRefMut<'b>
1 | /// A wrapper type for a mutably borrowed value from a `RefCell<T>`. |
我们可以按照之前的规则,来大致明白这段逻辑:
new的时候,这里是个RefMut, 必须没有Ref, 即 unused 才是合法的,它的状态改为UNUSED - 1. 即一个RefMut.drop的时候,增加BorrowFlag的值.BorrowRefMut持有& Cell<BorrowFlag>
try_borrow 类似:
1 |
|
1 | struct BorrowRef<'b> { |
new的时候+1并检查,如果之前是< 0的 writing 状态,返回 None. 否则返回BorrowRefdrop的时候borrow减少clone的时候++borrow