Skip to content

Commit 6bb06f4

Browse files
authored
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).
2 parents 63a83c5 + c625b97 commit 6bb06f4

File tree

1 file changed

+86
-0
lines changed

1 file changed

+86
-0
lines changed

library/core/src/cell.rs

+86
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,40 @@ impl<'b, T: ?Sized> Ref<'b, T> {
12611261
Ref { value: f(orig.value), borrow: orig.borrow }
12621262
}
12631263

1264+
/// Makes a new `Ref` for an optional component of the borrowed data. The
1265+
/// original guard is returned as an `Err(..)` if the closure returns
1266+
/// `None`.
1267+
///
1268+
/// The `RefCell` is already immutably borrowed, so this cannot fail.
1269+
///
1270+
/// This is an associated function that needs to be used as
1271+
/// `Ref::filter_map(...)`. A method would interfere with methods of the same
1272+
/// name on the contents of a `RefCell` used through `Deref`.
1273+
///
1274+
/// # Examples
1275+
///
1276+
/// ```
1277+
/// #![feature(cell_filter_map)]
1278+
///
1279+
/// use std::cell::{RefCell, Ref};
1280+
///
1281+
/// let c = RefCell::new(vec![1, 2, 3]);
1282+
/// let b1: Ref<Vec<u32>> = c.borrow();
1283+
/// let b2: Result<Ref<u32>, _> = Ref::filter_map(b1, |v| v.get(1));
1284+
/// assert_eq!(*b2.unwrap(), 2);
1285+
/// ```
1286+
#[unstable(feature = "cell_filter_map", reason = "recently added", issue = "81061")]
1287+
#[inline]
1288+
pub fn filter_map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Result<Ref<'b, U>, Self>
1289+
where
1290+
F: FnOnce(&T) -> Option<&U>,
1291+
{
1292+
match f(orig.value) {
1293+
Some(value) => Ok(Ref { value, borrow: orig.borrow }),
1294+
None => Err(orig),
1295+
}
1296+
}
1297+
12641298
/// Splits a `Ref` into multiple `Ref`s for different components of the
12651299
/// borrowed data.
12661300
///
@@ -1372,6 +1406,58 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
13721406
RefMut { value: f(value), borrow }
13731407
}
13741408

1409+
/// Makes a new `RefMut` for an optional component of the borrowed data. The
1410+
/// original guard is returned as an `Err(..)` if the closure returns
1411+
/// `None`.
1412+
///
1413+
/// The `RefCell` is already mutably borrowed, so this cannot fail.
1414+
///
1415+
/// This is an associated function that needs to be used as
1416+
/// `RefMut::filter_map(...)`. A method would interfere with methods of the
1417+
/// same name on the contents of a `RefCell` used through `Deref`.
1418+
///
1419+
/// # Examples
1420+
///
1421+
/// ```
1422+
/// #![feature(cell_filter_map)]
1423+
///
1424+
/// use std::cell::{RefCell, RefMut};
1425+
///
1426+
/// let c = RefCell::new(vec![1, 2, 3]);
1427+
///
1428+
/// {
1429+
/// let b1: RefMut<Vec<u32>> = c.borrow_mut();
1430+
/// let mut b2: Result<RefMut<u32>, _> = RefMut::filter_map(b1, |v| v.get_mut(1));
1431+
///
1432+
/// if let Ok(mut b2) = b2 {
1433+
/// *b2 += 2;
1434+
/// }
1435+
/// }
1436+
///
1437+
/// assert_eq!(*c.borrow(), vec![1, 4, 3]);
1438+
/// ```
1439+
#[unstable(feature = "cell_filter_map", reason = "recently added", issue = "81061")]
1440+
#[inline]
1441+
pub fn filter_map<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> Result<RefMut<'b, U>, Self>
1442+
where
1443+
F: FnOnce(&mut T) -> Option<&mut U>,
1444+
{
1445+
// FIXME(nll-rfc#40): fix borrow-check
1446+
let RefMut { value, borrow } = orig;
1447+
let value = value as *mut T;
1448+
// SAFETY: function holds onto an exclusive reference for the duration
1449+
// of its call through `orig`, and the pointer is only de-referenced
1450+
// inside of the function call never allowing the exclusive reference to
1451+
// escape.
1452+
match f(unsafe { &mut *value }) {
1453+
Some(value) => Ok(RefMut { value, borrow }),
1454+
None => {
1455+
// SAFETY: same as above.
1456+
Err(RefMut { value: unsafe { &mut *value }, borrow })
1457+
}
1458+
}
1459+
}
1460+
13751461
/// Splits a `RefMut` into multiple `RefMut`s for different components of the
13761462
/// borrowed data.
13771463
///

0 commit comments

Comments
 (0)