Skip to content

Commit

Permalink
Use Result and rename to filter_map
Browse files Browse the repository at this point in the history
The use of Result allows for making use of a reconstructed original value on failed
projections.
  • Loading branch information
udoprog committed Jan 15, 2021
1 parent 0660b8b commit e8757af
Showing 1 changed file with 34 additions and 18 deletions.
52 changes: 34 additions & 18 deletions library/core/src/cell.rs
Expand Up @@ -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<Vec<u32>> = c.borrow();
/// let b2: Option<Ref<u32>> = Ref::try_map(b1, |v| v.get(1));
/// assert_eq!(b2.as_deref(), Some(&2))
/// let b2: Result<Ref<u32>, _> = 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<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Option<Ref<'b, U>>
pub fn filter_map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Result<Ref<'b, U>, 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
Expand Down Expand Up @@ -1402,44 +1406,56 @@ 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};
///
/// let c = RefCell::new(vec![1, 2, 3]);
///
/// {
/// let b1: RefMut<Vec<u32>> = c.borrow_mut();
/// let mut b2: Option<RefMut<u32>> = RefMut::try_map(b1, |v| v.get_mut(1));
/// let mut b2: Result<RefMut<u32>, _> = 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<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> Option<RefMut<'b, U>>
pub fn filter_map<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> Result<RefMut<'b, U>, 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
Expand Down

0 comments on commit e8757af

Please sign in to comment.