qcell/lib.rs
1//! Statically-checked alternatives to [`RefCell`] and [`RwLock`].
2//!
3//! This crate provides four alternatives to [`RefCell`], each of
4//! which checks borrows from the cell at compile-time (statically)
5//! instead of checking them at runtime as [`RefCell`] does. The
6//! mechasism for checks is the same for all four. They only differ
7//! in how ownership is represented: [`QCell`] uses an integer ID,
8//! [`TCell`] and [`TLCell`] use a marker type, and [`LCell`] uses a
9//! Rust lifetime. Each approach has its advantages and
10//! disadvantages.
11//!
12//! Taking [`QCell`] as an example: [`QCell`] is a cell type where the
13//! cell contents are logically 'owned' for borrowing purposes by an
14//! instance of an owner type, [`QCellOwner`]. So the cell contents
15//! can only be accessed by making borrowing calls on that owner.
16//! This behaves similarly to borrowing fields from a structure, or
17//! borrowing elements from a `Vec`. However actually the only link
18//! between the objects is that a reference to the owner instance was
19//! provided when the cell was created. Effectively the
20//! borrowing-owner and dropping-owner are separated.
21//!
22//! This enables a pattern where the compiler can statically check
23//! mutable access to data stored behind `Rc` references (or other
24//! reference types) at compile-time. This pattern works as follows:
25//! The owner is kept on the stack and a mutable reference to it is
26//! passed down the stack to calls (for example as part of a context
27//! structure). This is fully checked at compile-time by the borrow
28//! checker. Then this static borrow checking is extended to the cell
29//! contents (behind `Rc`s) through using borrowing calls on the owner
30//! instance to access the cell contents. This gives a compile-time
31//! guarantee that access to the cell contents is safe.
32//!
33//! The alternative would be to use [`RefCell`], which panics if two
34//! mutable references to the same data are attempted. With
35//! [`RefCell`] there are no warnings or errors to detect the problem
36//! at compile-time. On the other hand, using [`QCell`] the error is
37//! detected at compile-time, but the restrictions are much stricter
38//! than they really need to be. For example it's not possible to
39//! borrow from more than a few different cells at the same time if
40//! they are protected by the same owner, which [`RefCell`] would
41//! allow (correctly). However if you are able to work within these
42//! restrictions (e.g. by keeping borrows active only for a short
43//! time), then the advantage is that there can never be a panic due
44//! to erroneous use of borrowing, because everything is checked by
45//! the compiler.
46//!
47//! Apart from [`QCell`] and [`QCellOwner`], this crate also provides
48//! [`TCell`] and [`TCellOwner`] which work the same but use a marker
49//! type instead of owner IDs, [`TLCell`] and [`TLCellOwner`] which
50//! also use a marker type but which are thread-local, and [`LCell`]
51//! and [`LCellOwner`] which use lifetimes. See the ["Comparison of
52//! cell types"](#comparison-of-cell-types) below.
53//!
54//! # Examples
55//!
56//! With [`RefCell`], this compiles but panics:
57//!
58//! ```should_panic
59//!# use std::rc::Rc;
60//!# use std::cell::RefCell;
61//! let item = Rc::new(RefCell::new(Vec::<u8>::new()));
62//! let mut iref = item.borrow_mut();
63//! test(&item);
64//! iref.push(1);
65//!
66//! fn test(item: &Rc<RefCell<Vec<u8>>>) {
67//! item.borrow_mut().push(2); // Panics here
68//! }
69//! ```
70//!
71//! With [`QCell`], it refuses to compile:
72//!
73//! ```compile_fail
74//!# use qcell::{QCell, QCellOwner};
75//!# use std::rc::Rc;
76//! let mut owner = QCellOwner::new();
77//!
78//! let item = Rc::new(QCell::new(&owner, Vec::<u8>::new()));
79//! let iref = owner.rw(&item);
80//! test(&mut owner, &item); // Compile error
81//! iref.push(1);
82//!
83//! fn test(owner: &mut QCellOwner, item: &Rc<QCell<Vec<u8>>>) {
84//! owner.rw(&item).push(2);
85//! }
86//! ```
87//!
88//! The solution in both cases is to make sure that the `iref` is not
89//! active when the call is made, but [`QCell`] uses standard
90//! compile-time borrow-checking to force the bug to be fixed. This
91//! is the main advantage of using these types.
92//!
93//! Here's a working version using [`TCell`] instead:
94//!
95#![cfg_attr(
96 feature = "std",
97 doc = "
98 ```
99# use qcell::{TCell, TCellOwner};
100# use std::rc::Rc;
101 struct Marker;
102 type ACell<T> = TCell<Marker, T>;
103 type ACellOwner = TCellOwner<Marker>;
104 let mut owner = ACellOwner::new();
105
106 let item = Rc::new(ACell::new(Vec::<u8>::new()));
107 let iref = owner.rw(&item);
108 iref.push(1);
109 test(&mut owner, &item);
110
111 fn test(owner: &mut ACellOwner, item: &Rc<ACell<Vec<u8>>>) {
112 owner.rw(&item).push(2);
113 }
114 ```
115"
116)]
117//!
118//! And the same thing again using [`LCell`]:
119//!
120//! ```
121//!# use qcell::{LCell, LCellOwner};
122//!# use std::rc::Rc;
123//! LCellOwner::scope(|mut owner| {
124//! let item = Rc::new(LCell::new(Vec::<u8>::new()));
125//! let iref = owner.rw(&item);
126//! iref.push(1);
127//! test(&mut owner, &item);
128//! });
129//!
130//! fn test<'id>(owner: &mut LCellOwner<'id>, item: &Rc<LCell<'id, Vec<u8>>>) {
131//! owner.rw(&item).push(2);
132//! }
133//! ```
134//!
135//! # Why this is safe
136//!
137//! This is the reasoning behind declaring this crate's interface
138//! safe:
139//!
140//! - Between the cell creation and destruction, the only way to
141//! access the contents (for read or write) is through the
142//! borrow-owner instance. So the borrow-owner is the exclusive
143//! gatekeeper of this data.
144//!
145//! - The borrowing calls require a `&` owner reference to return a
146//! `&` cell reference, or a `&mut` on the owner to return a `&mut`
147//! cell reference. So this is the same kind of borrow on both sides.
148//! The only borrow we allow for the cell is the borrow that Rust
149//! allows for the borrow-owner, and while that borrow is active, the
150//! borrow-owner and the cell's reference are blocked from further
151//! incompatible borrows. The contents of the cells act as if they
152//! were owned by the borrow-owner, just like elements within a `Vec`.
153//! So Rust's guarantees are maintained.
154//!
155//! - The borrow-owner has no control over when the cell's contents
156//! are dropped, so the borrow-owner cannot act as a gatekeeper to the
157//! data at that point. However this cannot clash with any active
158//! borrow on the data because whilst a borrow is active, the
159//! reference to the cell is effectively locked by Rust's borrow
160//! checking. If this is behind an `Rc`, then it's impossible for the
161//! last strong reference to be released until that borrow is
162//! released.
163//!
164//! If you can see a flaw in this reasoning or in the code, please
165//! raise an issue, preferably with test code which demonstrates the
166//! problem. MIRI in the Rust playground can report on some kinds of
167//! unsafety.
168//!
169//! # Comparison of cell types
170//!
171//! [`RefCell`] pros and cons:
172//!
173//! - Pro: Simple
174//! - Pro: Allows very flexible borrowing patterns
175//! - Pro: `no_std` support
176//! - Con: No compile-time borrowing checks
177//! - Con: Can panic due to distant code changes
178//! - Con: Runtime borrow checks and some cell space overhead
179//!
180//! [`QCell`] pros and cons:
181//!
182//! - Pro: Simple
183//! - Pro: Compile-time borrowing checks
184//! - Pro: Dynamic owner creation, not limited in any way
185//! - Pro: No lifetime annotations or type parameters required
186//! - Pro: `no_std` support
187//! - Con: Can only borrow up to 3 objects at a time
188//! - Con: Runtime owner checks and some cell space overhead
189//!
190//! [`TCell`] and [`TLCell`] pros and cons:
191//!
192//! - Pro: Compile-time borrowing checks
193//! - Pro: No overhead at runtime for borrowing or ownership checks
194//! - Pro: No cell space overhead
195//! - Pro: `no_std` support (via external crate)
196//! - Con: Can only borrow up to 3 objects at a time
197//! - Con: Uses singletons, either per-process (TCell) or per-thread
198//! (TLCell), meaning only one owner is allowed per thread or process
199//! per marker type. Code intended to be nested on the call stack
200//! must be parameterised with an external marker type.
201//!
202//! [`LCell`] pros and cons:
203//!
204//! - Pro: Compile-time borrowing checks
205//! - Pro: No overhead at runtime for borrowing or ownership checks
206//! - Pro: No cell space overhead
207//! - Pro: No need for singletons, meaning that one use does not limit other nested uses
208//! - Pro: `no_std` support
209//! - Con: Can only borrow up to 3 objects at a time
210//! - Con: Requires lifetime annotations on calls and structures
211//!
212//! Cell | Owner ID | Cell overhead | Borrow check | Owner check | Owner creation check
213//! ---|---|---|---|---|---
214//! `RefCell` | n/a | `usize` | Runtime | n/a | n/a
215//! `QCell` | integer | `usize` | Compile-time | Runtime | Runtime
216//! `TCell` or `TLCell` | marker type | none | Compile-time | Compile-time | Runtime
217//! `LCell` | lifetime | none | Compile-time | Compile-time | Compile-time
218//!
219//! Owner ergonomics:
220//!
221//! Cell | Owner type | Owner creation
222//! ---|---|---
223//! `RefCell` | n/a | n/a
224//! `QCell` | `QCellOwner` | `QCellOwner::new()`
225//! `TCell` or<br/>`TLCell` | `ACellOwner`<br/>(or `BCellOwner` or `CCellOwner` etc) | `struct MarkerA;`<br/>`type ACell<T> = TCell<MarkerA, T>;`<br/>`type ACellOwner = TCellOwner<MarkerA>;`<br/>`ACellOwner::new()`
226//! `LCell` | `LCellOwner<'id>` | `LCellOwner::scope(`\|`owner`\|` { ... })`
227//!
228//! Cell ergonomics:
229//!
230//! Cell | Cell type | Cell creation
231//! ---|---|---
232//! `RefCell` | `RefCell<T>` | `RefCell::new(v)`
233//! `QCell` | `QCell<T>` | `owner.cell(v)` or `QCell::new(&owner, v)`
234//! `TCell` or `TLCell` | `ACell<T>` | `owner.cell(v)` or `ACell::new(v)`
235//! `LCell` | `LCell<'id, T>` | `owner.cell(v)` or `LCell::new(v)`
236//!
237//! Borrowing ergonomics:
238//!
239//! Cell | Cell immutable borrow | Cell mutable borrow
240//! ---|---|---
241//! `RefCell` | `cell.borrow()` | `cell.borrow_mut()`
242//! `QCell` | `cell.ro(&owner)` or<br/>`owner.ro(&cell)` | `cell.rw(&mut owner)` or<br/>`owner.rw(&cell)`
243//! `TCell` or `TLCell` | `cell.ro(&owner)` or<br/>`owner.ro(&cell)` | `cell.rw(&mut owner)` or<br/>`owner.rw(&cell)`
244//! `LCell` | `cell.ro(&owner)` or<br/>`owner.ro(&cell)` | `cell.rw(&mut owner)` or<br/>`owner.rw(&cell)`
245//!
246//! # Multi-threaded use: Send and Sync
247//!
248//! Most often the cell-owner will be held by just one thread, and all
249//! access to cells will be made within that thread. However it is
250//! still safe to pass or share these objects between threads in some
251//! cases, where permitted by the contained type:
252//!
253//! Cell | Owner type | Cell type
254//! ---|---|---
255//! `RefCell` | n/a | Send
256//! `QCell` | Send + Sync | Send + Sync
257//! `TCell` | Send + Sync | Send + Sync
258//! `TLCell` | | Send
259//! `LCell` | Send + Sync | Send + Sync
260//!
261//! I am grateful for contributions from Github users [**Migi**] and
262//! [**pythonesque**] to justify the reasoning behind enabling Send
263//! and/or Sync. (`GhostCell` by [**pythonesque**] is a
264//! lifetime-based cell that predated `LCell`, but which was only
265//! [officially published in
266//! 2021](http://plv.mpi-sws.org/rustbelt/ghostcell/). The authors of
267//! that paper proved that the logical reasoning behind `GhostCell` is
268//! correct, which indirectly strengthens the theoretical
269//! justification for other similar cell types, such as the ones in
270//! this crate.)
271//!
272//! Here's an overview of the reasoning:
273//!
274//! - Unlike `RefCell` these cell types may be `Sync` because mutable
275//! access is protected by the cell owner. You can get mutable access
276//! to the cell contents only if you have mutable access to the cell
277//! owner. (Note that `Sync` is only available where the contained
278//! type is `Send + Sync`.)
279//!
280//! - The cell owner may be `Sync` because `Sync` only allows shared
281//! immutable access to the cell owner across threads. So there may
282//! exist `&QCell` and `&QCellOwner` references in two threads, but
283//! only immutable access to the cell contents is possible like that,
284//! so there is no soundness issue.
285//!
286//! - In general `Send` is safe because that is a complete transfer of
287//! some right from one thread to another (assuming the contained type
288//! is also `Send`).
289//!
290//! - `TLCell` is the exception because there can be a different owner
291//! with the same marker type in each thread, so owners must not be
292//! sent or shared. Also if two threads have `&TLCell` references to
293//! the same cell then mutable references to the contained data could
294//! be created in both threads which would break Rust's guarantees.
295//! So `TLCell` cannot be `Sync`. However it can be `Send` because in
296//! that case the right to access the data is being transferred
297//! completely from one thread to another.
298//!
299//! # Multi-threaded use: RwLock
300//!
301//! `QCell` and similar types can also be used as a replacement for
302//! `RwLock`. For example if you have a collection of
303//! `Arc<RwLock<T>>`, you can replace them with `Arc<QCell<T>>`.
304//! Essentially you're exchanging the fine-grained locking (one for
305//! every single `T`) for a coarse-grained lock around the
306//! `QCellOwner`. Depending on the access patterns, this might work
307//! out better or worse. For example if you often need to access
308//! several `T` instances in one logical operation, and there is low
309//! contention on the big lock, then it will work out better. Or if
310//! you already have `&mut` on the `struct` containing the
311//! `QCellOwner`, then you get access to the `T` instances essentially
312//! for free.
313//!
314//! # `no_std` support
315//!
316//! There are four levels at which **qcell** crate can be built:
317//!
318//! - Full `std` support, which is the default
319//!
320//! - `no_std` with
321//! [**exclusion-set**](https://crates.io/crates/exclusion-set), when
322//! built with `--no-default-features` and `--features exclusion-set`
323//!
324//! - `no_std` with `alloc`, when built with `--no-default-features`
325//! and `--features alloc`
326//!
327//! - `no_std` without `alloc`, when built with `--no-default-features`
328//!
329//! Both [`QCell`] and [`LCell`] support all four levels, and
330//! [`TCell`] is also available for the first two.
331//!
332//! # Origin of names
333//!
334//! "Q" originally referred to quantum entanglement, the idea being
335//! that this is a kind of remote ownership. "T" refers to it being
336//! type system based, "TL" thread-local, "L" to lifetime-based.
337//!
338//! # Unsafe code patterns blocked
339//!
340//! See the [`doctest_qcell`], [`doctest_tcell`], [`doctest_tlcell`],
341//! [`doctest_lcell`], [`doctest_qcell_noalloc`] and
342//! [`doctest_lcell_generativity`] modules, whose docs and doc-tests
343//! show which unsafe patterns are blocked.
344//!
345//! [`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
346//! [`RwLock`]: https://doc.rust-lang.org/std/sync/struct.RwLock.html
347//! [`QCell`]: struct.QCell.html
348//! [`QCellOwner`]: struct.QCellOwner.html
349//! [`TCell`]: struct.TCell.html
350//! [`TCellOwner`]: struct.TCellOwner.html
351//! [`TLCell`]: struct.TLCell.html
352//! [`TLCellOwner`]: struct.TLCellOwner.html
353//! [`LCell`]: struct.LCell.html
354//! [`LCellOwner`]: struct.LCellOwner.html
355//! [`doctest_qcell`]: doctest_qcell/index.html
356//! [`doctest_qcell_noalloc`]: doctest_qcell_noalloc/index.html
357//! [`doctest_tcell`]: doctest_tcell/index.html
358//! [`doctest_tlcell`]: doctest_tlcell/index.html
359//! [`doctest_lcell`]: doctest_lcell/index.html
360//! [`doctest_lcell_generativity`]: doctest_lcell_generativity/index.html
361//! [**Migi**]: https://github.com/Migi
362//! [**pythonesque**]: https://github.com/pythonesque
363
364#![cfg_attr(not(any(feature = "std", test)), no_std)]
365#![cfg_attr(docsrs, feature(doc_cfg))]
366#![deny(rust_2018_idioms)]
367
368#[cfg(feature = "alloc")]
369extern crate alloc;
370
371mod lcell;
372mod qcell;
373mod tcell;
374#[cfg(feature = "std")]
375mod tlcell;
376
377pub mod doctest_lcell;
378#[cfg(feature = "generativity")]
379pub mod doctest_lcell_generativity;
380#[cfg(feature = "alloc")]
381pub mod doctest_qcell;
382pub mod doctest_qcell_noalloc;
383#[cfg(feature = "std")]
384pub mod doctest_tcell;
385#[cfg(feature = "std")]
386pub mod doctest_tlcell;
387
388#[cfg(feature = "generativity")]
389pub extern crate generativity;
390
391// Used in LCell, TCell and TLCell. See the Rustonomicon chapters
392// "Subtyping and Variance"
393// (https://doc.rust-lang.org/nomicon/subtyping.html) and
394// "PhantomData"
395// (https://doc.rust-lang.org/nomicon/phantom-data.html).
396//
397// `fn(T) -> T` forces T to be treated as invariant. This does not
398// change the type of 'T', but changes what that type can be converted
399// into. Variance in Rust only applies to lifetimes, so this is about
400// blocking conversion of one lifetime into a larger or smaller one.
401// We need invariance in the marker type for `TCell` and `TLCell`
402// because otherwise it's possible for a malicious programmer to cheat
403// the singleton check and obtain undefined behaviour.
404//
405// `fn(T) -> T` is better than `Cell<T>` or `*mut T` because it passes
406// through the `UnwindSafe` trait unaffected.
407//
408// Needs an abstraction as a struct, since otherwise we'll get errors
409// regarding "function pointers cannot appear in constant functions"
410struct Invariant<T>(fn(T) -> T);
411
412pub use crate::lcell::LCell;
413pub use crate::lcell::LCellOwner;
414pub use crate::qcell::QCell;
415pub use crate::qcell::QCellOwnerID;
416pub use crate::qcell::QCellOwnerPinned;
417pub use crate::qcell::QCellOwnerSeq;
418pub use crate::tcell::TCell;
419pub use crate::tcell::TCellOwner;
420
421#[cfg(feature = "alloc")]
422pub use crate::qcell::QCellOwner;
423
424#[cfg(feature = "std")]
425pub use crate::{tlcell::TLCell, tlcell::TLCellOwner};
426
427// Static assertions on traits
428#[cfg(test)]
429mod assertions;