deferred_reference/lib.rs
1//! This crate helps with creating multiple mutable references to the contents of a variable without triggering undefined behavior.
2//! The Rust borrow rules dictate that it is undefined behavior to create more than one mutable reference to the same region,
3//! even if the mutable reference is not used. However, this can sometimes be a tad too restrictive if the programmer knows
4//! that two mutable references will not overlap. Using raw pointers, it is already possible to work-around the Rust borrow rules today,
5//! but this requires wizard-like skills and in-depth knowledge of handling raw pointers and this is more prone to error than
6//! using Rust references. With the introduction of non-lexical lifetimes in the Rust 2018 edition, the ergonomics around references
7//! have already significantly improved, but there are still some corner-cases where the programmer wished there was some way
8//! to create non-overlapping mutable references into the same location (e.g. disjoint indices of a slice or array), without
9//! resorting to manually managed raw pointers. In order to aid with this, this crate introduces the concept of a "*deferred reference*"[^1].
10//! A deferred reference is almost exactly like a regular reference (e.g. a `&T` or a `&mut T`), but it differs from a regular reference
11//! in the following ways:
12//! * A deferred reference is not an actual reference, it is merely a smart pointer tied to the lifetime of the location it points to
13//! (regular raw pointers always have a static lifetime, and can thus become dangling if the location it points to is moved or dropped).
14//! * It is allowed to keep multiple deferred mutable references around (as long as these are not dereferenced in a way so that
15//! these create an overlap between a mutable reference and another (de)reference).
16//!
17//! A deferred reference is embodied by an instance of the [Deferred] struct, which can be used like a regular [Reference].
18//! There exist only two types of references and therefore there are two flavors of [Deferred], namely `Deferred<&T>` and
19//! `Deferred<&mut T>`. Both of these can be used the same way as a regular reference, because `Deferred<&T>` implements
20//! the [Deref](core::ops::Deref) trait and `Deferred<&mut T>` implements both the [Deref](core::ops::Deref) trait and the
21//! [DerefMut](core::ops::DerefMut) trait.
22//!
23//! In order to obtain a `Deferred<&T>` or a `Deferred<&mut T>`, see the documentation at [Deferred] explaining the
24//! [constructors for deferred immutable references](Deferred#constructors-for-deferred-immutable-references) and
25//! [constructors for deferred mutable references](Deferred#constructors-for-deferred-mutable-references).
26//!
27//! # Arrays and slices
28//! The [Deferred] struct also provides some interesting functionality for deferred arrays and slices, for which it implements the
29//! [Index](core::ops::Index) and [IndexMut](core::ops::IndexMut) traits in a way that it only creates references to the
30//! queried indices but not to the other disjoint subslices. This allows multiple threads to simultaneouslt mutate the same array
31//! or slice as long as these threads don't create mutable references that overlap in index or in lifetime. Currently this functionality
32//! is available on stable Rust 1.51.0 for arrays `[T; N]` thanks to the introduction of the
33//! [`min_const_generics` feature](https://github.com/rust-lang/rfcs/blob/master/text/2000-const-generics.md), but to use this
34//! functionality on slices `[T]`, too, nightly Rust is required until the `slice_ptr_len` feature stabilizes. For more details, see the
35//! [methods available for deferred references to slices and arrays](Deferred#methods-only-available-for-deferred-references-to-slices-and-arrays).
36//!
37//! # Example
38//! Here is an example of how one might use the [Deferred] struct:
39//! ```
40//! use deferred_reference::{Defer, DeferMut, Deferred};
41//! use core::cell::UnsafeCell;
42//! use core::ops::DerefMut;
43//! let buffer = Box::new(UnsafeCell::new([0u8; 1024])); // the `Box` is optional
44//! // calling defer() or defer_mut() immutably borrows `buffer` for as long
45//! // as the returned `Deferred` is in use.
46//! let deferred: Deferred<&[u8; 1024]> = buffer.defer();
47//! // SAFETY: this is safe, because we promise not to create an overlapping mutable reference
48//! let mut deferred_mut: Deferred<&mut [u8; 1024]> = unsafe { buffer.defer_mut() };
49//! // both `deferred` and `deferred_mut` can be safely immutably dereferenced simultaneously:
50//! assert_eq!(&deferred[0], &deferred_mut[0]);
51//! // we can mutate the `buffer` through `deferred_mut` as any other array.
52//! // this implicity creates a temporary mutable reference into the array inside `buffer`.
53//! // even though an immutable reference to `buffer` exists, this is okay because
54//! // the inner array sits inside an `UnsafeCell` which allows interior mutability:
55//! deferred_mut[0] = 42;
56//! // and observe the change through `deferred`:
57//! assert_eq!(deferred[0], 42);
58//! // all this time, both deferred references are alive, but because
59//! // these are not actual references, this doesn't violate the Rust borrow
60//! // rules and this is not undefined behavior. The lifetimes of the mutable
61//! // and immutable references derived from the Deferred do not overlap,
62//! // so the Rust borrow rules are respected all this time.
63//! assert_eq!(&deferred[0], &deferred_mut[0]);
64//! // this also works for multiple deferred mutable references!
65//! // SAFETY: this is safe, because we promise not to create overlapping references
66//! let mut deferred_mut2 = unsafe { buffer.defer_mut() };
67//! // we can mutate the buffer through 2 distinct deferred mutable references
68//! // (as long as we don't do this at the same time!)
69//! deferred_mut[0] += 1; // the actual mutable borrow starts and ends here
70//! // the next line does not overlap with the previous, thanks to non-lexical lifetimes:
71//! deferred_mut2[0] += 1;
72//! assert_eq!(44, deferred[0]);
73//! assert_eq!(deferred_mut[0], deferred_mut2[0]);
74//! // however, the following is not okay, because this would create two overlapping
75//! // mutable references and this is undefined behavior (hence the use of `unsafe` above):
76//! //assert_eq!(&mut deferred_mut[0], &mut deferred_mut2[0]); // undefined behavior!
77//! // because `Deferred` implements the `Index` and `IndexMut` trait, it is possible
78//! // to create two references that overlap in lifetime, but are disjoint in index:
79//! assert_eq!(&mut deferred_mut[1], &mut deferred_mut2[2]); // indices are disjoint, so no UB
80//! // this is not possible with regular slice references, because these alias the entire slice:
81//! //assert_eq!(&mut deferred_mut.deref_mut()[1], &mut deferred_mut2.deref_mut()[2]); // UB!
82//! ```
83//!
84//! # Why not use `<[T]>::split_at_mut` instead of `Deferred`?
85//! The Rust core library function [split_at_mut](<slice::split_at_mut>) provides a convenient method to safely split a mutable reference
86//! into two mutable references. However, it has one big drawback: in order to call it, you must already have the mutable reference.
87//! This might not always be possible. For example, in a multi-threaded environment, some threads may want to temporarily keep an
88//! immutable reference to a slice index because these threads only read, while some threads need a temporary mutable reference into
89//! another slice index. This is not possible if one thread holds a mutable reference to the entire slice, because then the co-existance
90//! of any mutable and immutable references would trigger undefined behavior. With deferred references, this is no longer a problem, because an
91//! initial mutable reference is not required. In fact, the first mutable reference is not even created until the first time the
92//! `Deferred<&mut>` is dereferenced. This crate also provides a method [Deferred::split_at_mut] which can split a deferred reference
93//! to a slice or an array into two deferred references. This method does not create any intermediary references to the subslices.
94//!
95//! # `#![no_std]` environments
96//! This crate is entirely `#![no_std]` and does not depend on the `alloc` crate. No additional `Cargo.toml` features need to be configured
97//! in order to support `#![no_std]` environments. This crate also does not have any dependencies in its `Cargo.toml`.
98//!
99//! # Miri tested
100//! This crate is extensively tested using [Miri](https://github.com/rust-lang/miri) using the `-Zmiri-track-raw-pointers` flag:
101//! ```bash
102//! $ MIRIFLAGS="-Zmiri-track-raw-pointers" cargo miri test
103//! ```
104//! Miri follows the [Stacked Borrows](https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md) model
105//! (by Ralf Jung et al.) and so does this crate. If you happen to spot any violations of this model in this crate, feel free
106//! to open a Github issue!
107//!
108//! # Footnotes
109//! [^1]: The concept of "deferred references" is inspired by the concept of "[Deferred Borrows](https://c1f.net/pubs/ecoop2020_defborrow.pdf)"
110//! authored by Chris Fallin. However, these are not entirely the same concept. These differ in the sense that deferred borrows bring
111//! an extension to the Rust type system (called *static path-dependent types*) and its implementation is intended to live within the
112//! Rust compiler, while deferred references are implemented in Rust code that is already possible today with its existing type system.
113//! The trade-off made here is that this requires minimal use of `unsafe` code blocks with deferred references, while deferred borrows
114//! would work entirely within "safe Rust" if these were to be implemented in the Rust compiler. There are also some similarities between
115//! the two concepts: both concepts are statically applied during compile-time and due not incur any runtime overhead. Also, with both
116//! approaches an actual reference is not created until the reference is actually in use (i.e. dereferenced or borrowed for an extended
117//! period of time).
118
119#![no_std]
120#![doc(html_root_url = "https://docs.rs/deferred-reference/0.1.2")]
121// the `slice_ptr_len` feature is needed for deferred references to slices
122#![cfg_attr(feature = "slice_ptr_len", feature(slice_ptr_len))]
123// experimental: the `coerce_unsized` feature is need to unsize the reference
124// through [Deferred::unsize], because we can't implement [CoerceUnsized] on
125// [Deferred] (yet, this gives compiler errors).
126#![cfg_attr(feature = "coerce_unsized", feature(coerce_unsized))]
127
128#![deny(missing_docs)]
129#![forbid(clippy::missing_docs_in_private_items)]
130
131// the `alloc` crate is only used for tests, but it is not used by this crate otherwise.
132#[cfg(test)] #[macro_use] extern crate alloc;
133
134
135// from <https://rust-lang.github.io/unsafe-code-guidelines/glossary.html>:
136// "Aliasing occurs when one pointer or reference points to a "span" of memory that overlaps
137// with the span of another pointer or reference. A span of memory is similar to how a slice
138// works: there's a base byte address as well as a length in bytes.
139// Note: a full aliasing model for Rust, defining when aliasing is allowed and when not, has
140// not yet been defined. The purpose of this definition is to define when aliasing happens,
141// not when it is allowed. The most developed potential aliasing model so far is Stacked Borrows."
142
143mod core_traits_impl;
144pub use core_traits_impl::*;
145
146mod defer;
147pub use defer::*;
148
149mod defer_mut;
150pub use defer_mut::*;
151
152mod deferred;
153pub use deferred::*;
154
155mod pointer_length;
156pub use pointer_length::*;
157
158mod reference;
159pub use reference::*;
160
161mod slice_like;
162pub use slice_like::*;
163
164mod slice_like_impl;
165pub use slice_like_impl::*;
166
167mod slice_pointer_index;
168pub use slice_pointer_index::*;
169
170
171#[cfg(test)]
172mod tests {
173 use alloc::boxed::Box;
174 use core::{cell::UnsafeCell, fmt::{Debug, Pointer}, ops::{Deref, DerefMut}};
175
176 use super::*;
177
178 #[test]
179 fn example() {
180 use crate::{Deferred, DeferMut};
181 use core::cell::UnsafeCell;
182 const N: usize = 1024;
183 let buffer = UnsafeCell::new([0usize; N]);
184 // SAFETY: this is safe because there are no references into `buffer` yet
185 let mut deferred1: Deferred<&mut [usize; N]> = unsafe { buffer.defer_mut() };
186 // SAFETY: this is safe because we promise not to create overlap with mutable references
187 let mut deferred2: Deferred<&mut [usize; N]> = unsafe { deferred1.clone_unchecked() };
188 assert_eq!(&mut deferred1[0..10], &mut deferred2[10..20]); // subslices do not overlap
189 }
190
191 /// A method to assert that `t` is still being used.
192 fn use_it<T: Pointer>(t: T) {
193 assert!(!format!("{:p}", t).is_empty());
194 }
195
196 #[test]
197 fn doctest1() {
198 let buffer = Box::new(UnsafeCell::new([0u8; 1024])); // the `Box` is optional
199 // calling defer() or defer_mut() immutably borrows `buffer` for as long
200 // as the returned `Deferred` is in use.
201 let deferred: Deferred<&[u8; 1024]> = buffer.defer();
202 // SAFETY: this is safe, because we promise not to create overlapping references
203 let mut deferred_mut: Deferred<&mut [u8; 1024]> = unsafe { buffer.defer_mut() };
204 // both `deferred` and `deferred_mut` can be safely immutably dereferenced simultaneously:
205 assert_eq!(&deferred[0], &deferred_mut[0]);
206 // we can mutate the `buffer` through `deferred_mut` as any other array.
207 // this implicity creates a temporary mutable reference into the array inside `buffer`.
208 // even though an immutable reference to `buffer` exists, this is okay because
209 // the inner array sits inside an `UnsafeCell` which allows interior mutability:
210 deferred_mut[0] = 42;
211 // and observe the change through `deferred`:
212 assert_eq!(deferred[0], 42);
213 // all this time, both deferred references are alive, but because
214 // these are not actual references, this doesn't violate the Rust borrow
215 // rules and this is not undefined behavior. The lifetimes of the mutable
216 // and immutable references derived from the Deferred do not overlap,
217 // so the Rust borrow rules are respected all this time.
218 assert_eq!(&deferred[0], &deferred_mut[0]);
219 // this also works for multiple deferred mutable references!
220 // SAFETY: this is safe, because we promise not to create overlapping references
221 let mut deferred_mut2 = unsafe { buffer.defer_mut() };
222 // we can mutate the buffer through 2 distinct deferred mutable references
223 // (as long as we don't do this at the same time!)
224 deferred_mut[0] += 1; // the actual mutable borrow starts and ends here
225 // the next line does not overlap with the previous, thanks to non-lexical lifetimes:
226 deferred_mut2[0] += 1;
227 assert_eq!(44, deferred[0]);
228 assert_eq!(deferred_mut[0], deferred_mut2[0]);
229 // however, the following is not okay, because this would create two overlapping
230 // mutable references and this is undefined behavior (hence the use of `unsafe` above):
231 //assert_eq!(&mut deferred_mut[0], &mut deferred_mut2[0]); // undefined behavior!
232 // because `Deferred` implements the `Index` and `IndexMut` trait, it is possible
233 // to create two references that overlap in lifetime, but are disjoint in index:
234 assert_eq!(&mut deferred_mut[1], &mut deferred_mut2[2]); // indices are disjoint, so no UB
235 // this is not possible with regular slice references, because these alias the entire slice:
236 //assert_eq!(&mut deferred_mut.deref_mut()[1], &mut deferred_mut2.deref_mut()[2]); // UB!
237 }
238
239 #[test]
240 fn dyn_trait_ref() {
241 // multi trait dyn refs are not allowed yet, except for auto traits:
242 // error[E0225]: only auto traits can be used as additional traits in a trait object
243 // this results in a single vtable in the pointer, hence this is a sized fat pointer
244 let x: Box<UnsafeCell<dyn core::fmt::Debug + Send + Sync + Unpin>> = Box::new(UnsafeCell::new(core::fmt::Error));
245 let _ = x.defer();
246 }
247
248 #[cfg(feature = "coerce_unsized")]
249 #[test]
250 fn test_coerce_unsized() {
251 let b = Box::new(UnsafeCell::new([0u8; 1024]));
252 let deferred1: Deferred<&[u8; 1024]> = b.defer();
253 let deferred2: Deferred<&[u8]> = deferred1.unsize();
254 assert_eq!(deferred1.len(), deferred2.len());
255 assert_eq!(deferred1.deref(), deferred2.deref());
256 assert_eq!(deferred1.len(), deferred2.len());
257 }
258
259 #[test]
260 fn test_copy() {
261 let b = Box::new(UnsafeCell::new([0u8; 1024]));
262 let deferred2;
263 {
264 let deferred1 = b.defer();
265 deferred2 = deferred1; // copies
266 }
267 //let x = b.get_mut(); // cannot borrow `*b` as mutable because it is also borrowed as immutable
268 use_it(deferred2);
269 }
270
271 #[test]
272 fn test_box() {
273 let b = Box::new(UnsafeCell::new([0u8; 1024]));
274 let _x = unsafe { b.defer_mut() };
275 }
276
277 #[test]
278 fn deref_mut_unsafe_cell() {
279 let /*mut*/ buffer = UnsafeCell::new([0u8; 1024]);
280 let mut deferred = unsafe { buffer.defer_mut() };
281 let mut deferred2 = unsafe { buffer.defer_mut() };
282 // let tmp = buffer.get_mut(); // not possible
283 // let tmp = &mut buffer; // not possible :)
284 let _t = deferred.deref_mut();
285 let _t = deferred.deref();
286 let _t = deferred.deref_mut();
287 let _t = deferred.deref();
288 let _t = deferred.deref_mut();
289 let _t = deferred.deref();
290 use_it(&deferred);
291 assert_eq!(deferred[0], deferred2[1]); // okay, immutable access
292 assert_eq!(&deferred[0], &mut deferred2[1]); // okay, indices are disjoint
293 assert_eq!(&mut deferred[0], &mut deferred2[1]); // okay, indices are disjoint
294 // assert_eq!(&mut deferred[0], &mut deferred2[0]); // not okay, references overlap
295 }
296
297 mod mut_wrapper {
298 use super::*;
299 use super::super::Defer;
300
301 #[derive(Default, Debug)]
302 pub struct MaliciousContainer {
303 pub container: UnsafeCell<[u8; 32]>,
304 }
305
306 impl MaliciousContainer {
307 fn get_mut(&mut self) -> Deferred<&mut [u8; 32]> {
308 Deferred::from(self.container.get_mut())
309 }
310 }
311 impl Defer for MaliciousContainer {
312 type Target = [u8; 32];
313
314 fn defer(&self) -> Deferred<&Self::Target> {
315 self.container.defer()
316 }
317 }
318 unsafe impl DeferMut for MaliciousContainer {
319 unsafe fn defer_mut(&self) -> Deferred<&mut Self::Target> {
320 self.container.defer_mut()
321 }
322 }
323
324 #[test]
325 fn try_to_trigger_ub() {
326 let mut malicious_container = MaliciousContainer::default();
327 // this mutable alias works all the way through the [u8; 32] inside the UnsafeCell
328 // UnsafeCell only protects immutable references, not mutable references!
329 // we ensure it exists until the end of the scope.
330 let mut_ref = &mut malicious_container;
331
332 let x = mut_ref.get_mut();
333 let y = x.deref();
334 assert_eq!(&0, &y[0]);
335 use_it(&x);
336 use_it(&y);
337
338 let mut x = unsafe { mut_ref.defer_mut() };
339 let y = x.deref_mut();
340 assert_eq!(&0, &mut y[0]);
341 use_it(&y);
342 use_it(&x);
343
344 let x = mut_ref.defer();
345 let xcopy = x;
346 let y = x.deref();
347 assert_eq!(&0, &y[0]);
348 use_it(&x);
349 use_it(&y);
350
351 use_it(&mut_ref);
352 let xcopy = xcopy.deref();
353 use_it(&xcopy);
354 use_it(&mut_ref);
355 }
356 }
357}