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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! Zero-cost borrow-checking of aliased references supporting cyclic
//! construction.
//!
//! [`RecCell`] enforces borrow checking rules using a [`RecToken`], created
//! with [`RecToken::new`]. This creates a scope where the token grants
//! access to associated `RecCell`s:
//!
//! - `&RecToken` borrows a cell as `&T`.
//! - `&mut RecToken` borrows a cell as `&mut T`.
//!
//! Each cell is associated with exactly one token, and mismatched access is a
//! compile error. The correctness of the design above has been proven in the
//! [GhostCell paper].
//!
//! # Building cycles directly
//!
//! On top of that, this crate experiments with providing cyclic constructors
//! like [`RecCell::new_cyclic`], similar in spirit to
//! [`Rc::new_cyclic`][std::rc::Rc::new_cyclic], to initialize a
//! caller-provided [`MaybeUninit`][core::mem::MaybeUninit] slot. The
//! initialization closure receives a reference to the future cell, then returns
//! the value to write into it. That constructed value is allowed to refer to
//! the cell itself, creating a cycle. To prohibit accessing the cell's contents
//! before it is initialized, the cyclic constructors take the `RecToken` by
//! value and return it only when construction succeeds.
//!
//! Such an API comes with the following caveats:
//!
//! - Cyclic constructors write a `T` into a `&mut MaybeUninit<T>`.
//! `MaybeUninit` does not run `T`'s destructor, so cyclically initialized
//! values **leak unless you explicitly drop them**.
//!
//! - Cyclic constructors provide a reference to an uninitialized cell that must
//! not be accessed before initialization. If you fail to initialize the cell,
//! the token needs to be forever lost, so every cell under the same token
//! becomes permanently inaccessible by shared reference.
//!
//! - This crate does not allocate or own the storage. It is best paired with an
//! arena or any other allocation mechanism that provides storage with stable
//! addresses.
//!
//! # Cursors
//!
//! [`Cursor`]s are simple wrappers around (cell, &token) pairs and provide a
//! convenient way to traverse a structure of `RecCell`s. [`CursorMut`] provides
//! mutable access to the cell, and [`SliceCursor`] and [`SliceCursorMut`]
//! provide cursors over slices of `RecCell`s.
//!
//! From a safety perspective, they are just wrappers and anything that can be
//! done using them can also be done by managing the cell and token separately.
//!
//! # Initializing multiple cells cyclicly
//!
//! The [`RecToken::make_cyclic`] method generalizes [`RecCell::new_cyclic`] to
//! allow cyclicly initializing multiple cells at once. Arbitrary composition of
//! cyclic initializations is possible by making use of [`RecBuilder`], which is
//! used to check whether all initializations have been properly completed. If
//! any initialization fails, the builder is destroyed and the original
//! [`RecToken`] cannot be recovered, prohibiting access to uninitialized cells.
//! See [`RecToken::with_builder`] for more details.
//!
//! # Examples
//!
//! A self-referential node:
//!
//! ```
//! use std::mem::MaybeUninit;
//! use rec_cell::{RecCell, RecToken};
//!
//! struct Node<'a, 't> {
//! value: u32,
//! // No `Option` is needed, since we can cyclicly initialize the node to
//! // indirectly refer to itself, without modifying the node after node creation.
//! next: &'a RecCell<'t, Self>,
//! }
//!
//! RecToken::new(|token| {
//! let [mut node0, mut node1, mut node2] = [const { MaybeUninit::<Node>::uninit() }; 3];
//!
//! // Build a structure with node0 -> node1 -> node2 -> node0.
//! let (nodes, token) = RecCell::new_cyclic(&mut node0, token, |n0| {
//! let n2 = RecCell::from_mut(node2.write(Node { value: 20, next: n0 }));
//! let n1 = RecCell::from_mut(node1.write(Node { value: 10, next: n2 }));
//! let node0 = Node { value: 0, next: n1 };
//! ([n0, n1, n2], node0)
//! });
//!
//! // Walk the structure with a cursor.
//! let mut cursor = nodes[0].cursor(&token);
//! let target = [0, 10, 20, 0, 10, 20];
//! for i in 0..6 {
//! assert_eq!(cursor.get().value, target[i]);
//! cursor.update(|node| node.next);
//! }
//! });
//! ```
//!
//! # Prior art
//!
//! This crate is heavily based on [`ghost_cell`] and [`qcell::LCell`], which
//! provide similar methods except that they do not permit cyclic
//! initialization.
//!
//! `RecCell` is almost a drop-in replacement for the non-experimental parts of
//! `GhostCell`, except that `RecCell<'_, T>` requires `T: Sized` due to
//! `MaybeUninit<T>` requiring `T: Sized`. Use `[RecCell<'_, T>]` to manage a
//! slice `[T]`.
//!
//! [`ghost_cell`]: https://docs.rs/ghost-cell
//! [`qcell::LCell`]: https://docs.rs/qcell/latest/qcell/struct.LCell.html
//! [GhostCell paper]: https://plv.mpi-sws.org/rustbelt/ghostcell/
// All our errors arise from propagating closure errors.
extern crate std;
pub use crate::;