Rust std::cell

std::cell

今天病终于好了,不用上腹下泄了,来写点水文庆祝一下🎉

UnsafeCell

UnsafeCell 的动机在 F001 大佬的 这篇文章 里面提及了,提一下作者写的书还是很不错的,对我帮助很大。

UnsafeCell 结构如下所示:

1
2
3
4
5
6
#[lang = "unsafe_cell"]
#[stable(feature = "rust1", since = "1.0.0")]
#[repr(transparent)]
pub struct UnsafeCell<T: ?Sized> {
value: T,
}

注意一下 marker:

1
2
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Sync for UnsafeCell<T> {}

比较重要的方法是 get:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
impl<T: ?Sized> UnsafeCell<T> {
/// Gets a mutable pointer to the wrapped value.
///
/// This can be cast to a pointer of any kind.
/// Ensure that the access is unique (no active references, mutable or not)
/// when casting to `&mut T`, and ensure that there are no mutations
/// or mutable aliases going on when casting to `&T`
///
/// # Examples
///
/// ```
/// use std::cell::UnsafeCell;
///
/// let uc = UnsafeCell::new(5);
///
/// let five = uc.get();
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub const fn get(&self) -> *mut T {
// We can just cast the pointer from `UnsafeCell<T>` to `T` because of
// #[repr(transparent)]
self as *const UnsafeCell<T> as *const T as *mut T
}
}

repr(transparent) 资料在:

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 a f64 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 bare f64, 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 bare T in FFI context, which might be necessary to signal to the Rust side of things that this T value may unexpectedly be mutated.

std::cell::Cell

Cell implements interior mutability by moving values in and out of the Cell. To use references instead of values, one must use the RefCell type, acquiring a write lock before mutating. Cell provides methods to retrieve and change the current interior value:

  • For types that implement Copy, the get method retrieves the current interior value.
  • For types that implement Default, the take method replaces the current interior value with Default::default() and returns the replaced value.
  • For all types, the replace method replaces the current interior value and returns the replaced value and the into_inner method consumes the Cell and returns the interior value. Additionally, the set method replaces the interior value, dropping the replaced value.

对于 Cell 而言,不一定完全需要你实现 Copy 才能用,get 需要你实现 Copy, set 会移除原先内部的

1
2
3
4
5
#[stable(feature = "rust1", since = "1.0.0")]
#[repr(transparent)]
pub struct Cell<T: ?Sized> {
value: UnsafeCell<T>,
}

对应的几个 marker

1
2
3
4
5
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized> Send for Cell<T> where T: Send {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Sync for Cell<T> {}

可以看到,从结构设计来看, Cell 相对来说额外开销小一些。

Get/Set:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
impl<T:Copy> Cell<T> {
/// Returns a copy of the contained value.
///
/// # Examples
///
/// ```
/// use std::cell::Cell;
///
/// let c = Cell::new(5);
///
/// let five = c.get();
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn get(&self) -> T {
unsafe{ *self.value.get() }
}

// 省略
}

这个 get 需要实现 copy trait, 拿到 UnsafeCell 内部的数值。然后把这个旧值 drop 掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
impl<T> Cell<T> {
/// Sets the contained value.
///
/// # Examples
///
/// ```
/// use std::cell::Cell;
///
/// let c = Cell::new(5);
///
/// c.set(10);
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn set(&self, val: T) {
let old = self.replace(val);
drop(old);
}
}

看来我们需要看看 replace:

1
2
3
4
#[stable(feature = "move_cell", since = "1.17.0")]
pub fn replace(&self, val: T) -> T {
mem::replace(unsafe { &mut *self.value.get() }, val)
}

mem::replace 的文档如下:

1
pub fn replace<T>(dest: &mut T, src: T) -> T

Moves src into the referenced dest, returning the previous dest 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 for RefCells are tracked ‘at runtime’, unlike Rust’s native reference types which are entirely tracked statically, at compile time. Because RefCell 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
2
3
4
5
6
7
8
/// A mutable memory location with dynamically checked borrow rules
///
/// See the [module-level documentation](index.html) for more.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct RefCell<T: ?Sized> {
borrow: Cell<BorrowFlag>,
value: UnsafeCell<T>,
}

它相对 Cell 多维护了一个 borrow 字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Positive values represent the number of `Ref` active. Negative values
// represent the number of `RefMut` active. Multiple `RefMut`s can only be
// active at a time if they refer to distinct, nonoverlapping components of a
// `RefCell` (e.g., different ranges of a slice).
//
// `Ref` and `RefMut` are both two words in size, and so there will likely never
// be enough `Ref`s or `RefMut`s in existence to overflow half of the `usize`
// range. Thus, a `BorrowFlag` will probably never overflow or underflow.
// However, this is not a guarantee, as a pathological program could repeatedly
// create and then mem::forget `Ref`s or `RefMut`s. Thus, all code must
// explicitly check for overflow and underflow in order to avoid unsafety, or at
// least behave correctly in the event that overflow or underflow happens (e.g.,
// see BorrowRef::new).
type BorrowFlag = isize;
const UNUSED: BorrowFlag = 0;

可以看到:

  • UNUSED 表示这个 RefCell 并没有任何借用
  • Positive value: 表示 Ref, 这里可以推测是 &T 模式的借用存在
  • Nagative value: 表示 RefMut, 这里可以推测是 &mut T 模式的借用存在

让我们看看 try_borrow_mut:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/// Mutably borrows the wrapped value, returning an error if the value is currently borrowed.
///
/// The borrow lasts until the returned `RefMut` or all `RefMut`s derived
/// from it exit scope. The value cannot be borrowed while this borrow is
/// active.
///
/// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
///
/// # Examples
///
/// ```
/// use std::cell::RefCell;
///
/// let c = RefCell::new(5);
///
/// {
/// let m = c.borrow();
/// assert!(c.try_borrow_mut().is_err());
/// }
///
/// assert!(c.try_borrow_mut().is_ok());
/// ```
#[stable(feature = "try_borrow", since = "1.13.0")]
#[inline]
pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
match BorrowRefMut::new(&self.borrow) {
Some(b) => Ok(RefMut {
value: unsafe { &mut *self.value.get() },
borrow: b,
}),
None => Err(BorrowMutError { _private: () }),
}
}

这里有相当重要的 BorrowRefMut:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
struct BorrowRefMut<'b> {
borrow: &'b Cell<BorrowFlag>,
}

impl Drop for BorrowRefMut<'_> {
#[inline]
fn drop(&mut self) {
let borrow = self.borrow.get();
debug_assert!(is_writing(borrow));
self.borrow.set(borrow + 1);
}
}

impl<'b> BorrowRefMut<'b> {
#[inline]
fn new(borrow: &'b Cell<BorrowFlag>) -> Option<BorrowRefMut<'b>> {
// NOTE: Unlike BorrowRefMut::clone, new is called to create the initial
// mutable reference, and so there must currently be no existing
// references. Thus, while clone increments the mutable refcount, here
// we explicitly only allow going from UNUSED to UNUSED - 1.
match borrow.get() {
UNUSED => {
borrow.set(UNUSED - 1);
Some(BorrowRefMut { borrow })
},
_ => None,
}
}

// Clones a `BorrowRefMut`.
//
// This is only valid if each `BorrowRefMut` is used to track a mutable
// reference to a distinct, nonoverlapping range of the original object.
// This isn't in a Clone impl so that code doesn't call this implicitly.
#[inline]
fn clone(&self) -> BorrowRefMut<'b> {
let borrow = self.borrow.get();
debug_assert!(is_writing(borrow));
// Prevent the borrow counter from underflowing.
assert!(borrow != isize::min_value());
self.borrow.set(borrow - 1);
BorrowRefMut { borrow: self.borrow }
}
}
1
2
3
4
#[inline(always)]
fn is_writing(x: BorrowFlag) -> bool {
x < UNUSED
}

返回的是一个 RefMut, 带有值和 BorrowRefMut<'b>

1
2
3
4
5
6
7
8
/// A wrapper type for a mutably borrowed value from a `RefCell<T>`.
///
/// See the [module-level documentation](index.html) for more.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct RefMut<'b, T: ?Sized + 'b> {
value: &'b mut T,
borrow: BorrowRefMut<'b>,
}

我们可以按照之前的规则,来大致明白这段逻辑:

  1. new 的时候,这里是个 RefMut, 必须没有 Ref, 即 unused 才是合法的,它的状态改为 UNUSED - 1. 即一个 RefMut.
  2. drop 的时候,增加 BorrowFlag 的值.
  3. BorrowRefMut 持有 & Cell<BorrowFlag>

try_borrow 类似:

1
2
3
4
5
6
7
8
9
10
11
#[stable(feature = "try_borrow", since = "1.13.0")]
#[inline]
pub fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> {
match BorrowRef::new(&self.borrow) {
Some(b) => Ok(Ref {
value: unsafe { &*self.value.get() },
borrow: b,
}),
None => Err(BorrowError { _private: () }),
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
struct BorrowRef<'b> {
borrow: &'b Cell<BorrowFlag>,
}

impl<'b> BorrowRef<'b> {
#[inline]
fn new(borrow: &'b Cell<BorrowFlag>) -> Option<BorrowRef<'b>> {
let b = borrow.get().wrapping_add(1);
if !is_reading(b) {
// Incrementing borrow can result in a non-reading value (<= 0) in these cases:
// 1. It was < 0, i.e. there are writing borrows, so we can't allow a read borrow
// due to Rust's reference aliasing rules
// 2. It was isize::max_value() (the max amount of reading borrows) and it overflowed
// into isize::min_value() (the max amount of writing borrows) so we can't allow
// an additional read borrow because isize can't represent so many read borrows
// (this can only happen if you mem::forget more than a small constant amount of
// `Ref`s, which is not good practice)
None
} else {
// Incrementing borrow can result in a reading value (> 0) in these cases:
// 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read borrow
// 2. It was > 0 and < isize::max_value(), i.e. there were read borrows, and isize
// is large enough to represent having one more read borrow
borrow.set(b);
Some(BorrowRef { borrow })
}
}
}

impl Drop for BorrowRef<'_> {
#[inline]
fn drop(&mut self) {
let borrow = self.borrow.get();
debug_assert!(is_reading(borrow));
self.borrow.set(borrow - 1);
}
}

impl Clone for BorrowRef<'_> {
#[inline]
fn clone(&self) -> Self {
// Since this Ref exists, we know the borrow flag
// is a reading borrow.
let borrow = self.borrow.get();
debug_assert!(is_reading(borrow));
// Prevent the borrow counter from overflowing into
// a writing borrow.
assert!(borrow != isize::max_value());
self.borrow.set(borrow + 1);
BorrowRef { borrow: self.borrow }
}
}
  1. new 的时候 +1 并检查,如果之前是 < 0 的 writing 状态,返回 None. 否则返回 BorrowRef
  2. drop 的时候 borrow 减少
  3. clone 的时候 ++borrow