From 14999dd74b38ca79b80772f4f33425574faff89a Mon Sep 17 00:00:00 2001 From: Andreas Molzer Date: Wed, 29 Jan 2020 21:26:16 +0100 Subject: [PATCH 1/3] 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 e7eecf7540ad7..b1d799953e710 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> { From 99b4357f1763f7d98b9b78221207e09d075513b1 Mon Sep 17 00:00:00 2001 From: Andreas Molzer Date: Wed, 12 Feb 2020 16:56:09 +0100 Subject: [PATCH 2/3] Add tracking number, adjust documentation wording --- src/libcore/cell.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index b1d799953e710..17222b27b2d70 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -1249,8 +1249,9 @@ impl<'b, T: ?Sized> Ref<'b, T> { /// 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. + /// already immutably borrowed. It is not a good idea to leak more than a constant number of + /// references. The `RefCell` can be immutably borrowed again if only a smaller number of leaks + /// have occurred in total. /// /// This is an associated function that needs to be used as /// `Ref::leak(...)`. A method would interfere with methods of the @@ -1269,7 +1270,7 @@ impl<'b, T: ?Sized> Ref<'b, T> { /// assert!(cell.try_borrow().is_ok()); /// assert!(cell.try_borrow_mut().is_err()); /// ``` - #[unstable(feature = "cell_leak", issue = "none")] + #[unstable(feature = "cell_leak", issue = "69099")] 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, @@ -1385,7 +1386,7 @@ impl<'b, T: ?Sized> RefMut<'b, T> { /// /// assert!(cell.try_borrow_mut().is_err()); /// ``` - #[unstable(feature = "cell_leak", issue = "none")] + #[unstable(feature = "cell_leak", issue = "69099")] 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, From 329022dfad7199053cbe225e8d7d13ebbd5eb230 Mon Sep 17 00:00:00 2001 From: Andreas Molzer Date: Mon, 24 Feb 2020 11:23:47 +0100 Subject: [PATCH 3/3] Address method comments --- src/libcore/cell.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 17222b27b2d70..b6d5c6ae27db7 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -1272,9 +1272,8 @@ impl<'b, T: ?Sized> Ref<'b, T> { /// ``` #[unstable(feature = "cell_leak", issue = "69099")] 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. + // By forgetting this Ref we ensure that the borrow counter in the RefCell never goes back + // to UNUSED again. No further mutable references can be created from the original cell. mem::forget(orig.borrow); orig.value }