mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
Rollup merge of #78455 - udoprog:refcell-opt-map, r=KodrAus
Introduce {Ref, RefMut}::try_map for optional projections in RefCell This fills a usability gap of `RefCell` I've personally encountered to perform optional projections, mostly into collections such as `RefCell<Vec<T>>` or `RefCell<HashMap<U, T>>`: > This kind of API was briefly featured under Open questions in #10514 back in 2013 (!) ```rust let values = RefCell::new(vec![1, 2, 3, 4]); let b = Ref::opt_map(values.borrow(), |vec| vec.get(2)); ``` It primarily avoids this alternative approach to accomplish the same kind of projection which is both rather noisy and panicky: ```rust let values = RefCell::new(vec![1, 2, 3, 4]); let b = if values.get(2).is_some() { Some(Ref::map(values.borrow(), |vec| vec.get(2).unwrap())) } else { None }; ``` ### Open questions The naming `opt_map` is preliminary. I'm not aware of prior art in std to lean on here, but this name should probably be improved if this functionality is desirable. Since `opt_map` consumes the guard, and alternative syntax might be more appropriate which instead *tries* to perform the projection, allowing the original borrow to be recovered in case it fails: ```rust pub fn try_map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Result<Ref<'b, U>, Self> where F: FnOnce(&T) -> Option<&U>; ``` This would be more in line with the `try_map` method [provided by parking lot](https://docs.rs/lock_api/0/lock_api/struct.RwLockWriteGuard.html#method.try_map).
This commit is contained in:
commit
6bb06f4f23
@ -1261,6 +1261,40 @@ 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
|
||||
/// 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::filter_map(...)`. A method would interfere with methods of the same
|
||||
/// name on the contents of a `RefCell` used through `Deref`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![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: Result<Ref<u32>, _> = Ref::filter_map(b1, |v| v.get(1));
|
||||
/// assert_eq!(*b2.unwrap(), 2);
|
||||
/// ```
|
||||
#[unstable(feature = "cell_filter_map", reason = "recently added", issue = "81061")]
|
||||
#[inline]
|
||||
pub fn filter_map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Result<Ref<'b, U>, Self>
|
||||
where
|
||||
F: FnOnce(&T) -> Option<&U>,
|
||||
{
|
||||
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
|
||||
/// borrowed data.
|
||||
///
|
||||
@ -1372,6 +1406,58 @@ 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
|
||||
/// 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::filter_map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of a `RefCell` used through `Deref`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![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: Result<RefMut<u32>, _> = RefMut::filter_map(b1, |v| v.get_mut(1));
|
||||
///
|
||||
/// if let Ok(mut b2) = b2 {
|
||||
/// *b2 += 2;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(*c.borrow(), vec![1, 4, 3]);
|
||||
/// ```
|
||||
#[unstable(feature = "cell_filter_map", reason = "recently added", issue = "81061")]
|
||||
#[inline]
|
||||
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 = 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
|
||||
/// borrowed data.
|
||||
///
|
||||
|
Loading…
Reference in New Issue
Block a user