lending_cell/
lib.rs

1//! LendingCell is a mutable container that
2//! allows you to get an owned reference to the same
3//! object. When the owned reference is dropped,
4//! ownership returns to the original container.
5//!
6//! As opposed to a [`std::cell::Cell`] which moves Rust's
7//! _ownership_ rules to runtime, a `LendingCell` moves Rust's
8//! _lifetime_ rules to runtime.
9//!
10//! The value of a `LendingCell` is present at
11//! construction-time, but you can convert it to a
12//! `BorrowedCell<T>` by calling [`LendingCell::to_borrowed`].
13//! While that `BorrowedCell` lives (that is, until it is dropped),
14//! calling [`LendingCell::try_get`] will return `None`. The
15//! `BorrowedCell` has exclusive access to your type, as though you
16//! have a directly owned instance of the `T`, and so it is `Send`
17//! as long as `T` is Send. At last, when you drop the `BorrowedCell`,
18//! the `T` is returned to the `LendingCell`.
19//!
20//! If you drop your `LendingCell` before dropping the `BorrowedCell`,
21//! then the `T` is dropped at the time you drop the `BorrowedCell`.
22//!
23//! The invariants that Rust's memory safety rules enforce, like
24//! the single-mutable reference rule, are therefor partially ensured
25//! at compile time (you can't get two mutable references to the
26//! `T`), but also partially at runtime (while the `BorrowedCell`
27//! is active, the `LendingCell` behaves as though it is an `Option`
28//! containing `None`.
29//!
30//! # Why
31//! A general use for this crate is when you need a reference to a variable,
32//! but you're not able to use a lifetime. For example, Rust's [`Iterator`]
33//! type [doesn't allow a lifetime](https://rust-lang.github.io/rfcs/1598-generic_associated_types.html)
34//! on each `Item` that's independent of the container you're iterating over
35//! which means that Rust only allows you to iterate over a container
36//! with a lifetime exceeding your iterator, or to consume the container.
37//! What if instead you had to modify your
38
39use std::cell::UnsafeCell;
40use std::ops::{Deref, DerefMut};
41use std::sync::Arc;
42
43/// A container that allows borrowing without lifetimes.
44///
45/// ```rust
46/// # use lending_cell::*;
47/// let mut lender = LendingCell::new("borrowed");
48/// let borrowed = lender.to_borrowed();
49/// assert!(lender.try_get().is_none()); // `lender` is empty because it was borrowed
50/// assert_eq!(*borrowed, "borrowed"); // it's certain that `borrowed` is valid while in scope
51/// drop(borrowed); // dropping `borrowed` returns its value to `lender`
52/// assert!(lender.try_get().is_some());
53/// ```
54pub struct LendingCell<T> {
55    thing: Arc<UnsafeCell<T>>,
56}
57
58// SAFETY: type imitates T ownership
59unsafe impl<T: Sync> Sync for LendingCell<T> {}
60unsafe impl<T: Send> Send for LendingCell<T> {}
61
62impl<T> LendingCell<T> {
63    /// Creates a new LendingCell with the given value
64    pub fn new(thing: T) -> Self {
65        Self {
66            thing: Arc::new(UnsafeCell::new(thing)),
67        }
68    }
69
70    /// Get a reference to the contained value if it wasn't borrowed with
71    /// [`LendingCell::to_borrowed`]
72    pub fn try_get(&self) -> Option<&T> {
73        if Arc::strong_count(&self.thing) == 1 {
74            Some(unsafe { &*self.thing.get() })
75        } else {
76            None
77        }
78    }
79
80    /// Get a reference to the contained value if it wasn't borrowed with
81    /// [`LendingCell::to_borrowed`], otherwise panic
82    pub fn get(&self) -> &T {
83        self.try_get().unwrap()
84    }
85
86    /// Get a mutable reference the contained value if it wasn't borrowed with
87    /// [`LendingCell::to_borrowed`]
88    pub fn try_get_mut(&mut self) -> Option<&mut T> {
89        Arc::get_mut(&mut self.thing).map(|c| c.get_mut())
90    }
91
92    /// Get a mutable reference the contained value if it wasn't borrowed with
93    /// [`LendingCell::to_borrowed`], otherwise panic
94    pub fn get_mut(&mut self) -> &mut T {
95        self.try_get_mut().unwrap()
96    }
97
98    /// Take the contained value and returned it in an owned object if it
99    /// isn't already borrowed, otherwise panic.
100    pub fn to_borrowed(&mut self) -> BorrowedCell<T> {
101        self.try_to_borrowed().unwrap()
102    }
103
104    /// Take the contained value and returned it in an owned object if it
105    /// isn't already borrowed.
106    pub fn try_to_borrowed(&mut self) -> Option<BorrowedCell<T>> {
107        if Arc::strong_count(&self.thing) == 1 {
108            Some(BorrowedCell {
109                thing: Arc::clone(&self.thing),
110            })
111        } else {
112            None
113        }
114    }
115
116    /// Destroy the container and return the contained object if it isn't
117    /// being borrowed already. If it fails, return myself `LendingCell`
118    pub fn try_into_inner(self) -> Result<T, Self> {
119        Arc::try_unwrap(self.thing)
120            .map(|x| x.into_inner())
121            .map_err(|a| LendingCell { thing: a })
122    }
123}
124
125/// The container that ensures you have borrowed the [`LendingCell`].
126pub struct BorrowedCell<T> {
127    thing: Arc<UnsafeCell<T>>,
128}
129
130// SAFETY: type imitates either a mutable reference or an ownership
131unsafe impl<T: Send> Send for BorrowedCell<T> {}
132unsafe impl<T: Sync> Sync for BorrowedCell<T> {}
133
134impl<T> Deref for BorrowedCell<T> {
135    type Target = T;
136    fn deref(&self) -> &T {
137        unsafe { &*self.thing.get() }
138    }
139}
140
141impl<T> DerefMut for BorrowedCell<T> {
142    fn deref_mut(&mut self) -> &mut T {
143        unsafe { &mut *self.thing.get() }
144    }
145}