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
f64
field return nothing and take a pointer to be filled with the return value, whereas functions returning af64
return the floating-point number directly.This means that if someone wants to wrap a
f64
value 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
UnsafeCell
cannot be soundly used in place of a bareT
in FFI context, which might be necessary to signal to the Rust side of things that thisT
value may unexpectedly be mutated.
std::cell::Cell
Cell
implements interior mutability by moving values in and out of theCell
. To use references instead of values, one must use theRefCell
type, acquiring a write lock before mutating.Cell
provides methods to retrieve and change the current interior value:
- For types that implement
Copy
, theget
method retrieves the current interior value.- For types that implement
Default
, thetake
method replaces the current interior value withDefault::default()
and returns the replaced value.- For all types, the
replace
method replaces the current interior value and returns the replaced value and theinto_inner
method consumes theCell
and returns the interior value. Additionally, theset
method 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
src
into the referenceddest
, returning the previousdest
value.Neither value is dropped.
返回 UnsafeCell
保存的值,同时写入 UnsafeCell
.
std::cell::RefCell
RefCell
uses Rust’s lifetimes to implement ‘dynamic borrowing’, a process whereby one can claim temporary, exclusive, mutable access to the inner value. Borrows forRefCell
s are tracked ‘at runtime’, unlike Rust’s native reference types which are entirely tracked statically, at compile time. BecauseRefCell
borrows 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. 否则返回BorrowRef
drop
的时候borrow
减少clone
的时候++borrow