From 0660b8b5a592a004557147ebe128d5d8fd024cc5 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Wed, 28 Oct 2020 00:15:36 +0100 Subject: [PATCH 1/3] Introduce {Ref, RefMut}::try_map for optional projections --- library/core/src/cell.rs | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index c5ab7a39ff0ca..af421cb00f02c 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1261,6 +1261,36 @@ impl<'b, T: ?Sized> Ref<'b, T> { Ref { value: f(orig.value), borrow: orig.borrow } } + /// Makes a new `Ref` for an optional component of the borrowed data. + /// + /// The `RefCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `Ref::try_map(...)`. A method would interfere with methods of the same + /// name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(cell_try_map)] + /// + /// use std::cell::{RefCell, Ref}; + /// + /// let c = RefCell::new(vec![1, 2, 3]); + /// let b1: Ref> = c.borrow(); + /// let b2: Option> = Ref::try_map(b1, |v| v.get(1)); + /// assert_eq!(b2.as_deref(), Some(&2)) + /// ``` + #[unstable(feature = "cell_try_map", reason = "recently added", issue = "none")] + #[inline] + pub fn try_map(orig: Ref<'b, T>, f: F) -> Option> + where + F: FnOnce(&T) -> Option<&U>, + { + let value = f(orig.value)?; + Some(Ref { value, borrow: orig.borrow }) + } + /// Splits a `Ref` into multiple `Ref`s for different components of the /// borrowed data. /// @@ -1372,6 +1402,46 @@ impl<'b, T: ?Sized> RefMut<'b, T> { RefMut { value: f(value), borrow } } + /// Makes a new `RefMut` for an optional component of the borrowed data. + /// + /// The `RefCell` is already mutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `RefMut::try_map(...)`. A method would interfere with methods of the + /// same name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(cell_try_map)] + /// + /// use std::cell::{RefCell, RefMut}; + /// + /// let c = RefCell::new(vec![1, 2, 3]); + /// + /// { + /// let b1: RefMut> = c.borrow_mut(); + /// let mut b2: Option> = RefMut::try_map(b1, |v| v.get_mut(1)); + /// + /// if let Some(mut b2) = b2 { + /// *b2 += 2; + /// } + /// } + /// + /// assert_eq!(*c.borrow(), vec![1, 4, 3]); + /// ``` + #[unstable(feature = "cell_try_map", reason = "recently added", issue = "none")] + #[inline] + pub fn try_map(orig: RefMut<'b, T>, f: F) -> Option> + where + F: FnOnce(&mut T) -> Option<&mut U>, + { + // FIXME(nll-rfc#40): fix borrow-check + let RefMut { value, borrow } = orig; + let value = f(value)?; + Some(RefMut { value, borrow }) + } + /// Splits a `RefMut` into multiple `RefMut`s for different components of the /// borrowed data. /// From e8757af311601681f8002b699ab0d64838e75eab Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Thu, 17 Dec 2020 01:46:06 +0100 Subject: [PATCH 2/3] Use Result and rename to filter_map The use of Result allows for making use of a reconstructed original value on failed projections. --- library/core/src/cell.rs | 52 ++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index af421cb00f02c..c0a0e2f618f5d 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1261,34 +1261,38 @@ impl<'b, T: ?Sized> Ref<'b, T> { Ref { value: f(orig.value), borrow: orig.borrow } } - /// Makes a new `Ref` for an optional component of the borrowed data. + /// Makes a new `Ref` for an optional component of the borrowed data. The + /// original guard is returned as an `Err(..)` if the closure returns + /// `None`. /// /// The `RefCell` is already immutably borrowed, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `Ref::try_map(...)`. A method would interfere with methods of the same + /// `Ref::filter_map(...)`. A method would interfere with methods of the same /// name on the contents of a `RefCell` used through `Deref`. /// /// # Examples /// /// ``` - /// #![feature(cell_try_map)] + /// #![feature(cell_filter_map)] /// /// use std::cell::{RefCell, Ref}; /// /// let c = RefCell::new(vec![1, 2, 3]); /// let b1: Ref> = c.borrow(); - /// let b2: Option> = Ref::try_map(b1, |v| v.get(1)); - /// assert_eq!(b2.as_deref(), Some(&2)) + /// let b2: Result, _> = Ref::filter_map(b1, |v| v.get(1)); + /// assert_eq!(*b2.unwrap(), 2); /// ``` - #[unstable(feature = "cell_try_map", reason = "recently added", issue = "none")] + #[unstable(feature = "cell_filter_map", reason = "recently added", issue = "none")] #[inline] - pub fn try_map(orig: Ref<'b, T>, f: F) -> Option> + pub fn filter_map(orig: Ref<'b, T>, f: F) -> Result, Self> where F: FnOnce(&T) -> Option<&U>, { - let value = f(orig.value)?; - Some(Ref { value, borrow: orig.borrow }) + match f(orig.value) { + Some(value) => Ok(Ref { value, borrow: orig.borrow }), + None => Err(orig), + } } /// Splits a `Ref` into multiple `Ref`s for different components of the @@ -1402,18 +1406,20 @@ impl<'b, T: ?Sized> RefMut<'b, T> { RefMut { value: f(value), borrow } } - /// Makes a new `RefMut` for an optional component of the borrowed data. + /// Makes a new `RefMut` for an optional component of the borrowed data. The + /// original guard is returned as an `Err(..)` if the closure returns + /// `None`. /// /// The `RefCell` is already mutably borrowed, so this cannot fail. /// /// This is an associated function that needs to be used as - /// `RefMut::try_map(...)`. A method would interfere with methods of the + /// `RefMut::filter_map(...)`. A method would interfere with methods of the /// same name on the contents of a `RefCell` used through `Deref`. /// /// # Examples /// /// ``` - /// #![feature(cell_try_map)] + /// #![feature(cell_filter_map)] /// /// use std::cell::{RefCell, RefMut}; /// @@ -1421,25 +1427,35 @@ impl<'b, T: ?Sized> RefMut<'b, T> { /// /// { /// let b1: RefMut> = c.borrow_mut(); - /// let mut b2: Option> = RefMut::try_map(b1, |v| v.get_mut(1)); + /// let mut b2: Result, _> = RefMut::filter_map(b1, |v| v.get_mut(1)); /// - /// if let Some(mut b2) = b2 { + /// if let Ok(mut b2) = b2 { /// *b2 += 2; /// } /// } /// /// assert_eq!(*c.borrow(), vec![1, 4, 3]); /// ``` - #[unstable(feature = "cell_try_map", reason = "recently added", issue = "none")] + #[unstable(feature = "cell_filter_map", reason = "recently added", issue = "none")] #[inline] - pub fn try_map(orig: RefMut<'b, T>, f: F) -> Option> + pub fn filter_map(orig: RefMut<'b, T>, f: F) -> Result, Self> where F: FnOnce(&mut T) -> Option<&mut U>, { // FIXME(nll-rfc#40): fix borrow-check let RefMut { value, borrow } = orig; - let value = f(value)?; - Some(RefMut { value, borrow }) + let value = value as *mut T; + // SAFETY: function holds onto an exclusive reference for the duration + // of its call through `orig`, and the pointer is only de-referenced + // inside of the function call never allowing the exclusive reference to + // escape. + match f(unsafe { &mut *value }) { + Some(value) => Ok(RefMut { value, borrow }), + None => { + // SAFETY: same as above. + Err(RefMut { value: unsafe { &mut *value }, borrow }) + } + } } /// Splits a `RefMut` into multiple `RefMut`s for different components of the From c625b979aed9ad3c8380ea2239d9345d4cec695a Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 16 Jan 2021 10:40:36 +1000 Subject: [PATCH 3/3] add tracking issue to cell_filter_map --- library/core/src/cell.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index c0a0e2f618f5d..fa0fbaa35c95c 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1283,7 +1283,7 @@ impl<'b, T: ?Sized> Ref<'b, T> { /// let b2: Result, _> = Ref::filter_map(b1, |v| v.get(1)); /// assert_eq!(*b2.unwrap(), 2); /// ``` - #[unstable(feature = "cell_filter_map", reason = "recently added", issue = "none")] + #[unstable(feature = "cell_filter_map", reason = "recently added", issue = "81061")] #[inline] pub fn filter_map(orig: Ref<'b, T>, f: F) -> Result, Self> where @@ -1436,7 +1436,7 @@ impl<'b, T: ?Sized> RefMut<'b, T> { /// /// assert_eq!(*c.borrow(), vec![1, 4, 3]); /// ``` - #[unstable(feature = "cell_filter_map", reason = "recently added", issue = "none")] + #[unstable(feature = "cell_filter_map", reason = "recently added", issue = "81061")] #[inline] pub fn filter_map(orig: RefMut<'b, T>, f: F) -> Result, Self> where