1#![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
39pub 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 #[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 #[inline]
59 pub fn into_inner(self) -> T {
60 self.value.into_inner()
61 }
62
63 #[inline]
65 #[track_caller]
66 pub fn replace(&self, t: T) -> T {
67 mem::replace(&mut *self.borrow_mut(), t)
68 }
69
70 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
137 pub fn get_mut(&mut self) -> &mut T {
138 self.value.get_mut()
139 }
140
141 #[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 pub fn take(&self) -> T {
153 self.replace(Default::default())
154 }
155}
156
157impl<T: Clone> Clone for BetterRefCell<T> {
158 #[inline]
160 #[track_caller]
161 fn clone(&self) -> Self {
162 Self::new(self.borrow().clone())
163 }
164
165 #[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 #[inline]
176 fn default() -> Self {
177 Self::new(Default::default())
178 }
179}
180
181impl<T: ?Sized + PartialEq> PartialEq for BetterRefCell<T> {
182 #[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 #[inline]
194 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
195 self.borrow().partial_cmp(&*other.borrow())
196 }
197
198 #[inline]
200 fn lt(&self, other: &Self) -> bool {
201 *self.borrow() < *other.borrow()
202 }
203
204 #[inline]
206 fn le(&self, other: &Self) -> bool {
207 *self.borrow() <= *other.borrow()
208 }
209
210 #[inline]
212 fn gt(&self, other: &Self) -> bool {
213 *self.borrow() > *other.borrow()
214 }
215
216 #[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 #[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 fn from(t: T) -> Self {
234 Self::new(t)
235 }
236}
237
238impl<T: ?Sized> BetterRefCell<T> {
239 #[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 #[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 #[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 #[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#[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#[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}