better_refcell/
lib.rs

1//! A drop-in replacement for `RefCell` with safe *unborrow* and *reborrow* capabilities.
2//!
3//!
4//! # Features
5//!
6//! - Allows putting back a borrowed value and reborrowing it within a closure.
7//! - Fully compatible with the `RefCell` interface of the stable Rust standard library.
8//! - Zero dependencies.
9//!
10//!
11//! # Usage
12//!
13//! ```rust
14//! use better_refcell::BetterRefCell;
15//! use std::cell::*;
16//!
17//! let cell = BetterRefCell::new(42);
18//!
19//! let mut guard: RefMut<i32> = cell.borrow_mut();
20//! let mut_reference: &mut i32 = &mut *guard;
21//!
22//! let ret = cell.unborrow(mut_reference, || {
23//!     let mut guard = cell.borrow_mut();
24//!     assert_eq!(*guard, 42);
25//!     *guard += 1;
26//!     format!("Returns {guard}")
27//! });
28//!
29//! assert_eq!(*guard, 43);
30//! assert_eq!(ret, "Returns 43");
31//! ```
32
33#![no_std]
34#![allow(clippy::missing_safety_doc, clippy::missing_errors_doc)]
35use core::cell::{BorrowError, BorrowMutError, Cell, Ref, RefCell, RefMut, UnsafeCell};
36use core::ptr::{self, NonNull};
37use core::{cmp, mem};
38
39/// An enhanced `RefCell` with double-borrowing support.
40pub struct BetterRefCell<T: ?Sized> {
41    ptr: Cell<Option<NonNull<T>>>,
42    state: Cell<RefCell<()>>,
43    value: UnsafeCell<T>,
44}
45
46impl<T> BetterRefCell<T> {
47    /// Equivalent to [`RefCell::new`].
48    #[inline]
49    pub const fn new(value: T) -> Self {
50        Self {
51            ptr: Cell::new(None),
52            state: Cell::new(RefCell::new(())),
53            value: UnsafeCell::new(value),
54        }
55    }
56
57    /// Equivalent to [`RefCell::into_inner`].
58    #[inline]
59    pub fn into_inner(self) -> T {
60        self.value.into_inner()
61    }
62
63    /// Equivalent to [`RefCell::replace`].
64    #[inline]
65    #[track_caller]
66    pub fn replace(&self, t: T) -> T {
67        mem::replace(&mut *self.borrow_mut(), t)
68    }
69
70    /// Equivalent to [`RefCell::replace_with`].
71    #[inline]
72    #[track_caller]
73    pub fn replace_with<F: FnOnce(&mut T) -> T>(&self, f: F) -> T {
74        let mut_borrow = &mut *self.borrow_mut();
75        let replacement = f(mut_borrow);
76        mem::replace(mut_borrow, replacement)
77    }
78
79    /// Equivalent to [`RefCell::swap`].
80    #[inline]
81    pub fn swap(&self, other: &Self) {
82        mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut());
83    }
84}
85
86impl<T: ?Sized> BetterRefCell<T> {
87    /// Equivalent to [`RefCell::borrow`].
88    #[inline]
89    #[track_caller]
90    pub fn borrow(&self) -> Ref<'_, T> {
91        let state = unsafe { &*self.state.as_ptr() };
92        Ref::map(state.borrow(), |()| unsafe {
93            self.as_ptr().as_ref().unwrap_unchecked()
94        })
95    }
96
97    /// Equivalent to [`RefCell::try_borrow`].
98    #[inline]
99    pub fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> {
100        let state = unsafe { &*self.state.as_ptr() };
101        Ok(Ref::map(state.try_borrow()?, |()| unsafe {
102            self.as_ptr().as_ref().unwrap_unchecked()
103        }))
104    }
105
106    /// Equivalent to [`RefCell::borrow_mut`].
107    #[inline]
108    #[track_caller]
109    pub fn borrow_mut(&self) -> RefMut<'_, T> {
110        let state = unsafe { &*self.state.as_ptr() };
111        RefMut::map(state.borrow_mut(), |()| unsafe {
112            self.as_ptr().as_mut().unwrap_unchecked()
113        })
114    }
115
116    /// Equivalent to [`RefCell::try_borrow_mut`].
117    #[inline]
118    pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
119        let state = unsafe { &*self.state.as_ptr() };
120        Ok(RefMut::map(state.try_borrow_mut()?, |()| unsafe {
121            self.as_ptr().as_mut().unwrap_unchecked()
122        }))
123    }
124
125    /// Equivalent to [`RefCell::as_ptr`].
126    #[inline]
127    pub fn as_ptr(&self) -> *mut T {
128        if let Some(ptr) = self.ptr.get() {
129            ptr.as_ptr()
130        } else {
131            self.value.get()
132        }
133    }
134
135    /// Equivalent to [`RefCell::get_mut`].
136    #[inline]
137    pub fn get_mut(&mut self) -> &mut T {
138        self.value.get_mut()
139    }
140
141    /// Equivalent to [`RefCell::try_borrow_unguarded`].
142    #[inline]
143    pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, BorrowError> {
144        let state = unsafe { &*self.state.as_ptr() };
145        unsafe { state.try_borrow_unguarded()? };
146        Ok(unsafe { self.as_ptr().as_ref().unwrap_unchecked() })
147    }
148}
149
150impl<T: Default> BetterRefCell<T> {
151    /// Equivalent to [`RefCell::take`].
152    pub fn take(&self) -> T {
153        self.replace(Default::default())
154    }
155}
156
157impl<T: Clone> Clone for BetterRefCell<T> {
158    /// Equivalent to [`RefCell::clone`].
159    #[inline]
160    #[track_caller]
161    fn clone(&self) -> Self {
162        Self::new(self.borrow().clone())
163    }
164
165    /// Equivalent to [`RefCell::clone_from`].
166    #[inline]
167    #[track_caller]
168    fn clone_from(&mut self, source: &Self) {
169        self.get_mut().clone_from(&source.borrow());
170    }
171}
172
173impl<T: Default> Default for BetterRefCell<T> {
174    /// Equivalent to [`RefCell::default`].
175    #[inline]
176    fn default() -> Self {
177        Self::new(Default::default())
178    }
179}
180
181impl<T: ?Sized + PartialEq> PartialEq for BetterRefCell<T> {
182    /// Equivalent to [`RefCell::eq`].
183    #[inline]
184    fn eq(&self, other: &Self) -> bool {
185        *self.borrow() == *other.borrow()
186    }
187}
188
189impl<T: ?Sized + Eq> Eq for BetterRefCell<T> {}
190
191impl<T: ?Sized + PartialOrd> PartialOrd for BetterRefCell<T> {
192    /// Equivalent to [`RefCell::partial_cmp`].
193    #[inline]
194    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
195        self.borrow().partial_cmp(&*other.borrow())
196    }
197
198    /// Equivalent to [`RefCell::lt`].
199    #[inline]
200    fn lt(&self, other: &Self) -> bool {
201        *self.borrow() < *other.borrow()
202    }
203
204    /// Equivalent to [`RefCell::le`].
205    #[inline]
206    fn le(&self, other: &Self) -> bool {
207        *self.borrow() <= *other.borrow()
208    }
209
210    /// Equivalent to [`RefCell::gt`].
211    #[inline]
212    fn gt(&self, other: &Self) -> bool {
213        *self.borrow() > *other.borrow()
214    }
215
216    /// Equivalent to [`RefCell::ge`].
217    #[inline]
218    fn ge(&self, other: &Self) -> bool {
219        *self.borrow() >= *other.borrow()
220    }
221}
222
223impl<T: ?Sized + Ord> Ord for BetterRefCell<T> {
224    /// Equivalent to [`RefCell::cmp`].
225    #[inline]
226    fn cmp(&self, other: &Self) -> cmp::Ordering {
227        self.borrow().cmp(&*other.borrow())
228    }
229}
230
231impl<T> From<T> for BetterRefCell<T> {
232    /// Equivalent to [`RefCell::from`].
233    fn from(t: T) -> Self {
234        Self::new(t)
235    }
236}
237
238impl<T: ?Sized> BetterRefCell<T> {
239    /// Temporarily re-grant exclusive access to the internal value, allowing `borrow_mut`/`try_borrow_mut` again in `f`.
240    ///
241    /// # Panics
242    ///
243    /// This function panics only if any of the following conditions are violated:
244    /// 1. `borrowed` must point to the internal value stored in `self`.
245    /// 2. All borrow guards obtained from `self` within `f` must be dropped before `f` returns.
246    #[track_caller]
247    pub fn unborrow<R>(&self, borrowed: &mut T, f: impl FnOnce() -> R) -> R {
248        if !ptr::eq(self.value.get(), borrowed) {
249            panic_different_address()
250        }
251        let ptr = self.ptr.replace(Some(borrowed.into()));
252        let state = self.state.take();
253        let result = f();
254        let state = self.state.replace(state);
255        if state.try_borrow_mut().is_ok() {
256            self.ptr.set(ptr);
257            result
258        } else {
259            self.state.set(state);
260            panic_borrow_guard_leaked()
261        }
262    }
263
264    /// Temporarily re-grant exclusive access to the internal value, allowing `borrow_mut`/`try_borrow_mut` again in `f`.
265    ///
266    /// # Safety
267    ///
268    /// This function is safe to call if all of the following conditions are met:
269    /// 1. `borrowed` must point to the internal value stored in `self`.
270    /// 2. All borrow guards obtained from `self` within `f` must be dropped before `f` returns.
271    /// 3. Any borrow guards obtained from `self` outside of `f` must not be used within `f`.
272    #[track_caller]
273    pub unsafe fn unborrow_unchecked<R>(&self, borrowed: &mut T, f: impl FnOnce() -> R) -> R {
274        let ptr = self.ptr.replace(Some(borrowed.into()));
275        let state = self.state.take();
276        let result = f();
277        self.state.set(state);
278        self.ptr.set(ptr);
279        result
280    }
281
282    /// Temporarily re-grant shared access to the internal value, allowing `borrow`/`try_borrow` again in `f`.
283    ///
284    /// # Panics
285    ///
286    /// This function panics only if any of the following conditions are violated:
287    /// 1. `borrowed` must point to the internal value stored in `self`.
288    /// 2. If `borrowed` is (transitively) originated from `RefMut`, then all `Ref`s obtained from `self` within `f` must be dropped before `f` returns.
289    #[track_caller]
290    pub fn unborrow_ref<R>(&self, borrowed: &T, f: impl FnOnce() -> R) -> R {
291        if !ptr::eq(self.value.get(), borrowed) {
292            panic_different_address()
293        }
294        if unsafe { &mut *self.state.as_ptr() }.try_borrow().is_ok() {
295            return f();
296        }
297        let ptr = self.ptr.replace(Some(borrowed.into()));
298        let state = self.state.take();
299        let guard = unsafe { (*self.state.as_ptr()).try_borrow().unwrap_unchecked() };
300        let result = f();
301        drop(guard);
302        let state = self.state.replace(state);
303        if state.try_borrow_mut().is_ok() {
304            self.ptr.set(ptr);
305            result
306        } else {
307            self.state.set(state);
308            panic_borrow_guard_leaked()
309        }
310    }
311
312    /// Temporarily re-grant shared access to the internal value, allowing `borrow`/`try_borrow` again in `f`.
313    ///
314    /// # Safety
315    ///
316    /// This function is safe to call if all of the following conditions are met:
317    /// 1. `borrowed` must point to the internal value stored in `self`.
318    /// 2. If `borrowed` is (transitively) originated from `RefMut`, then all `Ref`s obtained from `self` within `f` must be dropped before `f` returns.
319    /// 3. Any borrow guards obtained from `self` outside of `f` must not be used within `f`.
320    #[track_caller]
321    pub unsafe fn unborrow_ref_unchecked<R>(&self, borrowed: &T, f: impl FnOnce() -> R) -> R {
322        if unsafe { &mut *self.state.as_ptr() }.try_borrow().is_ok() {
323            return f();
324        }
325        let ptr = self.ptr.replace(Some(borrowed.into()));
326        let state = self.state.take();
327        let result = f();
328        self.state.set(state);
329        self.ptr.set(ptr);
330        result
331    }
332}
333
334#[inline(never)]
335#[track_caller]
336#[cold]
337fn panic_different_address() -> ! {
338    panic!("reference is pointing to a different address")
339}
340
341#[inline(never)]
342#[track_caller]
343#[cold]
344fn panic_borrow_guard_leaked() -> ! {
345    panic!("borrow guard leaked in closure")
346}
347
348/// A macro to unborrow multiple instances at once, using either [`BetterRefCell::unborrow`] or [`BetterRefCell::unborrow_ref`].
349///
350/// The references to be unborrowed must be explicitly prefixed with `&` or `&mut` and should align with the order of their respective sources.
351///
352/// # Examples
353/// ```rust
354/// use better_refcell::{BetterRefCell, unborrow_all};
355///
356/// let cell_1 = BetterRefCell::new(1);
357/// let cell_2 = BetterRefCell::new(2);
358///
359/// let mut var_1 = cell_1.borrow_mut();
360/// let mut var_2 = cell_2.borrow_mut();
361///
362/// unborrow_all!((), (), move || {});
363///
364/// unborrow_all!(&cell_1, &var_1, || {});
365/// unborrow_all!(&cell_1, &mut var_1, || {});
366///
367/// unborrow_all!((&cell_1,), (&var_1), || {});
368/// unborrow_all!((&cell_1,), (&mut var_1), || {});
369///
370/// unborrow_all!((&cell_1, &cell_2), (&var_1, &mut var_2), || {});
371/// unborrow_all!((&cell_1, &cell_2), (&mut var_1, &var_2), || {});
372/// ```
373#[macro_export]
374macro_rules! unborrow_all {
375    ((), (), $closure:expr $(,)?) => {
376        $closure()
377    };
378
379    (($cell:expr, $($cells:expr),+ $(,)?), (&$var:expr, $($vars:tt)+), $closure:expr $(,)?) => {
380        $crate::unborrow_all!($cell, &$var, || {
381            $crate::unborrow_all!(($($cells),+), ($($vars)+), $closure)
382        })
383    };
384    (($cell:expr, $($cells:expr),+ $(,)?), (&mut $var:expr, $($vars:tt)+), $closure:expr $(,)?) => {
385        $crate::unborrow_all!($cell, &mut $var, || {
386            $crate::unborrow_all!(($($cells),+), ($($vars)+), $closure)
387        })
388    };
389    (($($cells:expr),+ $(,)?), ($var:expr, $($vars:tt)+), $closure:expr $(,)?) => {
390        $crate::unborrow_all!(@error_no_prefix $var)
391    };
392
393    (($cell:expr $(,)?), (&$var:expr $(,)?), $closure:expr $(,)?) => {
394        $crate::unborrow_all!($cell, &$var, $closure)
395    };
396    (($cell:expr $(,)?), (&mut $var:expr $(,)?), $closure:expr $(,)?) => {
397        $crate::unborrow_all!($cell, &mut $var, $closure)
398    };
399    (($cell:expr $(,)?), ($var:expr $(,)?), $closure:expr $(,)?) => {
400        $crate::unborrow_all!(@error_no_prefix $var)
401    };
402
403    ($cell:expr, &$var:expr, $closure:expr $(,)?) => {
404        $crate::BetterRefCell::unborrow_ref($cell, &$var, $closure)
405    };
406    ($cell:expr, &mut $var:expr, $closure:expr $(,)?) => {
407        $crate::BetterRefCell::unborrow($cell, &mut $var, $closure)
408    };
409    ($cell:expr, $var:expr, $closure:expr $(,)?) => {
410        $crate::unborrow_all!(@error_no_prefix $var)
411    };
412
413    (@error_no_prefix $var:expr) => {
414        ::core::compile_error!(::core::concat!(
415            "consider prefixing `",
416            ::core::stringify!($var),
417            "` with `&` or `&mut`",
418        ))
419    }
420}
421
422/// A macro to unborrow multiple instances at once, using either [`BetterRefCell::unborrow_unchecked`] or [`BetterRefCell::unborrow_ref_unchecked`].
423///
424/// The references to be unborrowed must be explicitly prefixed with `&` or `&mut` and should align with the order of their respective sources.
425///
426/// # Examples
427/// ```rust
428/// use better_refcell::{BetterRefCell, unborrow_all_unchecked};
429///
430/// let cell_1 = BetterRefCell::new(1);
431/// let cell_2 = BetterRefCell::new(2);
432///
433/// let mut var_1 = cell_1.borrow_mut();
434/// let mut var_2 = cell_2.borrow_mut();
435///
436/// unsafe {
437///     unborrow_all_unchecked!((), (), move || {});
438///
439///     unborrow_all_unchecked!(&cell_1, &var_1, || {});
440///     unborrow_all_unchecked!(&cell_1, &mut var_1, || {});
441///
442///     unborrow_all_unchecked!((&cell_1,), (&var_1), || {});
443///     unborrow_all_unchecked!((&cell_1,), (&mut var_1), || {});
444///
445///     unborrow_all_unchecked!((&cell_1, &cell_2), (&var_1, &mut var_2), || {});
446///     unborrow_all_unchecked!((&cell_1, &cell_2), (&mut var_1, &var_2), || {});
447/// }
448/// ```
449#[macro_export]
450macro_rules! unborrow_all_unchecked {
451    ((), (), $closure:expr $(,)?) => {
452        $closure()
453    };
454
455    (($cell:expr, $($cells:expr),+ $(,)?), (&$var:expr, $($vars:tt)+), $closure:expr $(,)?) => {
456        $crate::unborrow_all_unchecked!($cell, &$var, || {
457            $crate::unborrow_all_unchecked!(($($cells),+), ($($vars)+), $closure)
458        })
459    };
460    (($cell:expr, $($cells:expr),+ $(,)?), (&mut $var:expr, $($vars:tt)+), $closure:expr $(,)?) => {
461        $crate::unborrow_all_unchecked!($cell, &mut $var, || {
462            $crate::unborrow_all_unchecked!(($($cells),+), ($($vars)+), $closure)
463        })
464    };
465    (($($cells:expr),+ $(,)?), ($var:expr, $($vars:tt)+), $closure:expr $(,)?) => {
466        $crate::unborrow_all_unchecked!(@error_no_prefix $var)
467    };
468
469    (($cell:expr $(,)?), (&$var:expr $(,)?), $closure:expr $(,)?) => {
470        $crate::unborrow_all_unchecked!($cell, &$var, $closure)
471    };
472    (($cell:expr $(,)?), (&mut $var:expr $(,)?), $closure:expr $(,)?) => {
473        $crate::unborrow_all_unchecked!($cell, &mut $var, $closure)
474    };
475    (($cell:expr $(,)?), ($var:expr $(,)?), $closure:expr $(,)?) => {
476        $crate::unborrow_all_unchecked!(@error_no_prefix $var)
477    };
478
479    ($cell:expr, &$var:expr, $closure:expr $(,)?) => {
480        $crate::BetterRefCell::unborrow_ref_unchecked($cell, &$var, $closure)
481    };
482    ($cell:expr, &mut $var:expr, $closure:expr $(,)?) => {
483        $crate::BetterRefCell::unborrow_unchecked($cell, &mut $var, $closure)
484    };
485    ($cell:expr, $var:expr, $closure:expr $(,)?) => {
486        $crate::unborrow_all!(@error_no_prefix $var)
487    };
488
489    (@error_no_prefix $var:expr) => {
490        ::core::compile_error!(::core::concat!(
491            "consider prefixing `",
492            ::core::stringify!($var),
493            "` with `&` or `&mut`",
494        ))
495    }
496}
497
498#[cfg(test)]
499mod tests {
500    use super::*;
501
502    #[test]
503    fn unborrow() {
504        let cell = BetterRefCell::new(0);
505        let mut guard = cell.borrow_mut();
506        cell.unborrow(&mut guard, || {
507            let mut guard = cell.borrow_mut();
508            assert_eq!(*guard, 0);
509            *guard += 1;
510        });
511        assert_eq!(*guard, 1);
512    }
513
514    #[test]
515    #[should_panic = "reference is pointing to a different address"]
516    fn unborrow_by_invalid_reference() {
517        let cell = BetterRefCell::new(0);
518        cell.unborrow(&mut 0, || {});
519    }
520
521    #[test]
522    #[should_panic = "borrow guard leaked"]
523    fn unborrow_and_leak_ref_guard() {
524        let cell = BetterRefCell::new(0);
525        cell.unborrow(&mut cell.borrow_mut(), || cell.borrow());
526    }
527
528    #[test]
529    #[should_panic = "borrow guard leaked"]
530    fn unborrow_and_leak_mut_guard() {
531        let cell = BetterRefCell::new(0);
532        cell.unborrow(&mut cell.borrow_mut(), || cell.borrow_mut());
533    }
534
535    #[test]
536    fn unborrow_ref_by_ref() {
537        let cell = BetterRefCell::new(0);
538        let guard = cell.borrow();
539        cell.unborrow_ref(&guard, || {
540            let guard = cell.borrow();
541            assert_eq!(*guard, 0);
542        });
543        assert_eq!(*guard, 0);
544    }
545
546    #[test]
547    fn unborrow_ref_by_mut() {
548        let cell = BetterRefCell::new(0);
549        let guard = cell.borrow_mut();
550        cell.unborrow_ref(&guard, || {
551            let guard = cell.borrow();
552            assert_eq!(*guard, 0);
553        });
554        assert_eq!(*guard, 0);
555    }
556
557    #[test]
558    #[should_panic = "reference is pointing to a different address"]
559    fn unborrow_ref_by_invalid_reference() {
560        let cell = BetterRefCell::new(0);
561        cell.unborrow_ref(&0, || {});
562    }
563
564    #[test]
565    fn unborrow_ref_by_ref_and_leak_guard() {
566        let cell = BetterRefCell::new(0);
567        cell.unborrow_ref(&cell.borrow(), || cell.borrow());
568    }
569
570    #[test]
571    #[should_panic = "borrow guard leaked"]
572    fn unborrow_ref_by_mut_and_leak_guard() {
573        let cell = BetterRefCell::new(0);
574        cell.unborrow_ref(&cell.borrow_mut(), || cell.borrow());
575    }
576
577    #[test]
578    fn unborrow_ref_and_drop_outer_guard() {
579        let cell = BetterRefCell::new(0);
580        let outer_guard = cell.borrow();
581        cell.unborrow_ref(&cell.borrow(), || {
582            drop(outer_guard);
583            assert!(cell.try_borrow_mut().is_err());
584        });
585        assert!(cell.try_borrow_mut().is_ok());
586    }
587}