From 14999dd74b38ca79b80772f4f33425574faff89a Mon Sep 17 00:00:00 2001 From: Andreas Molzer Date: Wed, 29 Jan 2020 21:26:16 +0100 Subject: [PATCH] Add methods to leak RefCell borrows to references Usually, references to the interior are only created by the `Deref` and `DerefMut` impl of the guards `Ref` and `RefMut`. Note that `RefCell` already has to cope with leaks of such guards which, when it occurs, effectively makes it impossible to ever acquire a mutable guard or any guard for `Ref` and `RefMut` respectively. It is already safe to use this to create a reference to the inner of the ref cell that lives as long as the reference to the `RefCell` itself, e.g. ```rust fn leak(r: &RefCell) -> Option<&usize> { let guard = r.try_borrow().ok()?; let leaked = Box::leak(Box::new(guard)); Some(&*leaked) } ``` The newly added methods allow the same reference conversion without an indirection over a leaked allocation and composing with both borrow and try_borrow without additional method combinations. --- src/libcore/cell.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index e7eecf7540ad..b1d799953e71 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -1245,6 +1245,38 @@ impl<'b, T: ?Sized> Ref<'b, T> { let borrow = orig.borrow.clone(); (Ref { value: a, borrow }, Ref { value: b, borrow: orig.borrow }) } + + /// Convert into a reference to the underlying data. + /// + /// The underlying `RefCell` can never be mutably borrowed from again and will always appear + /// already immutably borrowed. It can still be immutably borrowed until more than `isize::MAX` + /// `Ref`s of this `RefCell` have been leaked, through this function or another leak, in total. + /// + /// This is an associated function that needs to be used as + /// `Ref::leak(...)`. A method would interfere with methods of the + /// same name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(cell_leak)] + /// use std::cell::{RefCell, Ref}; + /// let cell = RefCell::new(0); + /// + /// let value = Ref::leak(cell.borrow()); + /// assert_eq!(*value, 0); + /// + /// assert!(cell.try_borrow().is_ok()); + /// assert!(cell.try_borrow_mut().is_err()); + /// ``` + #[unstable(feature = "cell_leak", issue = "none")] + pub fn leak(orig: Ref<'b, T>) -> &'b T { + // By forgetting this BorrowRefMut we ensure that the borrow counter in the RefCell never + // goes back to UNUSED again. No further references can be created from the original cell, + // making the current borrow the only reference for the remaining lifetime. + mem::forget(orig.borrow); + orig.value + } } #[unstable(feature = "coerce_unsized", issue = "27732")] @@ -1330,6 +1362,37 @@ impl<'b, T: ?Sized> RefMut<'b, T> { let borrow = orig.borrow.clone(); (RefMut { value: a, borrow }, RefMut { value: b, borrow: orig.borrow }) } + + /// Convert into a mutable reference to the underlying data. + /// + /// The underlying `RefCell` can not be borrowed from again and will always appear already + /// mutably borrowed, making the returned reference the only to the interior. + /// + /// This is an associated function that needs to be used as + /// `RefMut::leak(...)`. A method would interfere with methods of the + /// same name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(cell_leak)] + /// use std::cell::{RefCell, RefMut}; + /// let cell = RefCell::new(0); + /// + /// let value = RefMut::leak(cell.borrow_mut()); + /// assert_eq!(*value, 0); + /// *value = 1; + /// + /// assert!(cell.try_borrow_mut().is_err()); + /// ``` + #[unstable(feature = "cell_leak", issue = "none")] + pub fn leak(orig: RefMut<'b, T>) -> &'b mut T { + // By forgetting this BorrowRefMut we ensure that the borrow counter in the RefCell never + // goes back to UNUSED again. No further references can be created from the original cell, + // making the current borrow the only reference for the remaining lifetime. + mem::forget(orig.borrow); + orig.value + } } struct BorrowRefMut<'b> {