about summary refs log tree commit diff
path: root/library
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-08-15 16:16:29 +1000
committerGitHub <noreply@github.com>2025-08-15 16:16:29 +1000
commit324bf2b9fd8bf9661e7045c8a93f5ff0ec1a8ca5 (patch)
tree8747cf5367ff40c47dafd5cc5f70c5c7b33ba610 /library
parent3507a749b365aae4eefa96ab700a9315d3280ee7 (diff)
parentf6bdffdcba7498fd3fe7b18bca966b23da6c463f (diff)
downloadrust-324bf2b9fd8bf9661e7045c8a93f5ff0ec1a8ca5.tar.gz
rust-324bf2b9fd8bf9661e7045c8a93f5ff0ec1a8ca5.zip
Rollup merge of #118087 - GrigorenkoPV:refcell_try_map, r=Mark-Simulacrum
Add Ref/RefMut try_map method

Tracking issue:  rust-lang/rust#143801

A more generalized version of [`filter_map`](https://doc.rust-lang.org/stable/core/cell/struct.Ref.html#method.filter_map), which allows to return some data on failure.

## Safety

As far as I can tell, `E` cannot contain any `'b` data, so it is impossible to duplicate the `&'b [mut]` reference into the `RefCell`'s  data.

Other than this `E`, everything is analogous to the already stable `filter_map`.

## `Try` / `Residual`

I have considered generalizing this to use the `Try` & `Residual` just like rust-lang/rust#79711 does for `array::try_map`, but it does not seem to be possible: we want to essentially `.map_err(|e| (orig, e))` but this does not seem to be supported with `Try`. (Plus I am not even sure if it is possible to express the fact that `&U` in `Try::Output` would have to have the same lifetime as the `&T` input of `F`.)

## ACP

~As far as I can tell, this [is not mandatory](https://std-dev-guide.rust-lang.org/development/feature-lifecycle.html#suitability-for-the-standard-library), and the implementation is small enough to probably be smaller than the doc I would have to write.~

~https://github.com/rust-lang/libs-team/issues/341~

https://github.com/rust-lang/libs-team/issues/586
Diffstat (limited to 'library')
-rw-r--r--library/core/src/cell.rs93
1 files changed, 93 insertions, 0 deletions
diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs
index c639d50cc3d..9c578dcdc2a 100644
--- a/library/core/src/cell.rs
+++ b/library/core/src/cell.rs
@@ -1573,6 +1573,47 @@ impl<'b, T: ?Sized> Ref<'b, T> {
         }
     }
 
+    /// Tries to makes a new `Ref` for a component of the borrowed data.
+    /// On failure, the original guard is returned alongside with the error
+    /// returned by the closure.
+    ///
+    /// 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(refcell_try_map)]
+    /// use std::cell::{RefCell, Ref};
+    /// use std::str::{from_utf8, Utf8Error};
+    ///
+    /// let c = RefCell::new(vec![0xF0, 0x9F, 0xA6 ,0x80]);
+    /// let b1: Ref<'_, Vec<u8>> = c.borrow();
+    /// let b2: Result<Ref<'_, str>, _> = Ref::try_map(b1, |v| from_utf8(v));
+    /// assert_eq!(&*b2.unwrap(), "🦀");
+    ///
+    /// let c = RefCell::new(vec![0xF0, 0x9F, 0xA6]);
+    /// let b1: Ref<'_, Vec<u8>> = c.borrow();
+    /// let b2: Result<_, (Ref<'_, Vec<u8>>, Utf8Error)> = Ref::try_map(b1, |v| from_utf8(v));
+    /// let (b3, e) = b2.unwrap_err();
+    /// assert_eq!(*b3, vec![0xF0, 0x9F, 0xA6]);
+    /// assert_eq!(e.valid_up_to(), 0);
+    /// ```
+    #[unstable(feature = "refcell_try_map", issue = "143801")]
+    #[inline]
+    pub fn try_map<U: ?Sized, E>(
+        orig: Ref<'b, T>,
+        f: impl FnOnce(&T) -> Result<&U, E>,
+    ) -> Result<Ref<'b, U>, (Self, E)> {
+        match f(&*orig) {
+            Ok(value) => Ok(Ref { value: NonNull::from(value), borrow: orig.borrow }),
+            Err(e) => Err((orig, e)),
+        }
+    }
+
     /// Splits a `Ref` into multiple `Ref`s for different components of the
     /// borrowed data.
     ///
@@ -1734,6 +1775,58 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
         }
     }
 
+    /// Tries to makes a new `RefMut` for a component of the borrowed data.
+    /// On failure, the original guard is returned alongside with the error
+    /// returned by the closure.
+    ///
+    /// 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(refcell_try_map)]
+    /// use std::cell::{RefCell, RefMut};
+    /// use std::str::{from_utf8_mut, Utf8Error};
+    ///
+    /// let c = RefCell::new(vec![0x68, 0x65, 0x6C, 0x6C, 0x6F]);
+    /// {
+    ///     let b1: RefMut<'_, Vec<u8>> = c.borrow_mut();
+    ///     let b2: Result<RefMut<'_, str>, _> = RefMut::try_map(b1, |v| from_utf8_mut(v));
+    ///     let mut b2 = b2.unwrap();
+    ///     assert_eq!(&*b2, "hello");
+    ///     b2.make_ascii_uppercase();
+    /// }
+    /// assert_eq!(*c.borrow(), "HELLO".as_bytes());
+    ///
+    /// let c = RefCell::new(vec![0xFF]);
+    /// let b1: RefMut<'_, Vec<u8>> = c.borrow_mut();
+    /// let b2: Result<_, (RefMut<'_, Vec<u8>>, Utf8Error)> = RefMut::try_map(b1, |v| from_utf8_mut(v));
+    /// let (b3, e) = b2.unwrap_err();
+    /// assert_eq!(*b3, vec![0xFF]);
+    /// assert_eq!(e.valid_up_to(), 0);
+    /// ```
+    #[unstable(feature = "refcell_try_map", issue = "143801")]
+    #[inline]
+    pub fn try_map<U: ?Sized, E>(
+        mut orig: RefMut<'b, T>,
+        f: impl FnOnce(&mut T) -> Result<&mut U, E>,
+    ) -> Result<RefMut<'b, U>, (Self, E)> {
+        // 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(&mut *orig) {
+            Ok(value) => {
+                Ok(RefMut { value: NonNull::from(value), borrow: orig.borrow, marker: PhantomData })
+            }
+            Err(e) => Err((orig, e)),
+        }
+    }
+
     /// Splits a `RefMut` into multiple `RefMut`s for different components of the
     /// borrowed data.
     ///