|
| 1 | +# Deferred Reference |
| 2 | +This crate helps with creating multiple mutable references to the contents of a variable without triggering undefined behavior. |
| 3 | +The Rust borrow rules dictate that it is undefined behavior to create more than one mutable reference to the same region, |
| 4 | +even if the mutable reference is not used. However, this can sometimes be a tad too restrictive if the programmer knows |
| 5 | +that two mutable references will not overlap. Using raw pointers, it is already possible to work-around the Rust borrow rules today, |
| 6 | +but this requires wizard-like skills and in-depth knowledge of handling raw pointers and this is more prone to error than |
| 7 | +using Rust references. With the introduction of non-lexical lifetimes in the Rust 2018 edition, the ergonomics around references |
| 8 | +have already significantly improved, but there are still some corner-cases where the programmer wished there was some way |
| 9 | +to create non-overlapping mutable references into the same location (e.g. disjoint indices of a slice or array), without |
| 10 | +resorting to manually managed raw pointers. In order to aid with this, this crate introduces the concept of a |
| 11 | +"*deferred reference*"<sup >[1](#footnote1)</a></sup>. A deferred reference is almost exactly like a regular reference |
| 12 | +(e.g. a `&T` or a `&mut T`), but it differs from a regular reference in the following ways: |
| 13 | +* A deferred reference is not an actual reference, it is merely a smart pointer tied to the lifetime of the location it points to |
| 14 | + (regular raw pointers always have a static lifetime, and can thus become dangling if the location it points to is moved or dropped). |
| 15 | +* It is allowed to keep multiple deferred mutable references around (as long as these are not dereferenced in a way so that |
| 16 | + these create an overlap between a mutable reference and another (de)reference). |
| 17 | + |
| 18 | +## Getting started |
| 19 | +If you're on nightly Rust, add the following dependency to your `Cargo.toml`: |
| 20 | + |
| 21 | +```toml |
| 22 | +[dependencies] |
| 23 | +deferred_reference = { version = "0.1.0" } |
| 24 | +``` |
| 25 | + |
| 26 | +This crate uses some unstable features, but it also works on stable Rust with less features. |
| 27 | +For using this crate in stable Rust you need to disable the unstable nightly features using the `default-features` flag, like so: |
| 28 | + |
| 29 | +```toml |
| 30 | +[dependencies] |
| 31 | +deferred_reference = { version = "0.1.0", default-features = false } |
| 32 | +``` |
| 33 | + |
| 34 | +Please see the documentation for this crate on how to get started with some concrete code examples. |
| 35 | + |
| 36 | +## `#![no_std]` environments |
| 37 | +This crate is entirely `#![no_std]` and does not depend on the `alloc` crate. No additional `Cargo.toml` features need to be configured |
| 38 | +in order to support `#![no_std]` environments. This crate also does not have any dependencies in its `Cargo.toml`. |
| 39 | + |
| 40 | +## Miri tested |
| 41 | +This crate is extensively tested using [Miri](https://github.com/rust-lang/miri) using the `-Zmiri-track-raw-pointers` flag: |
| 42 | +```bash |
| 43 | +$ MIRIFLAGS="-Zmiri-track-raw-pointers" cargo miri test |
| 44 | +``` |
| 45 | +Miri follows the [Stacked Borrows](https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md) model |
| 46 | +(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 |
| 47 | +to open a Github issue! |
| 48 | + |
| 49 | +## License |
| 50 | +This project is licensed under the MIT license. Please see the file `LICENSE.md` in the root of this project for the full license text. |
| 51 | + |
| 52 | +## Contributing |
| 53 | +It is encouraged to open a Github issue if you run into problems or if you have a feature request. |
| 54 | +At this point, this project does not accept pull requests yet. Please check back later! |
| 55 | + |
| 56 | +## Footnotes |
| 57 | +<a name="footnote1"></a> |
| 58 | +[1]: The concept of "deferred references" is inspired by the concept of "[Deferred Borrows](https://c1f.net/pubs/ecoop2020_defborrow.pdf)" |
| 59 | + authored by Chris Fallin. However, these are not entirely the same concept. These differ in the sense that deferred borrows bring |
| 60 | + an extension to the Rust type system (called *static path-dependent types*) and its implementation is intended to live within the |
| 61 | + Rust compiler, while deferred references are implemented in Rust code that is already possible today with its existing type system. |
| 62 | + The trade-off made here is that this requires minimal use of `unsafe` code blocks with deferred references, while deferred borrows |
| 63 | + would work entirely within "safe Rust" if these were to be implemented in the Rust compiler. There are also some similarities between |
| 64 | + the two concepts: both concepts are statically applied during compile-time and due not incur any runtime overhead. Also, with both |
| 65 | + approaches an actual reference is not created until the reference is actually in use (i.e. dereferenced or borrowed for an extended |
| 66 | + period of time). |
0 commit comments