From d5d5feab74af7de786dba10b82eebf7b44032677 Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Thu, 24 Jul 2025 17:09:17 -0400 Subject: [PATCH] add Box::(try_)map --- library/alloc/src/boxed.rs | 91 +++++++++++++++++++++++++++++++++++++- library/alloc/src/lib.rs | 1 + 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 3db37f1d16f3d..0248a34b373cb 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -194,8 +194,8 @@ use core::hash::{Hash, Hasher}; use core::marker::{Tuple, Unsize}; use core::mem::{self, SizedTypeProperties}; use core::ops::{ - AsyncFn, AsyncFnMut, AsyncFnOnce, CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, - DerefPure, DispatchFromDyn, LegacyReceiver, + AsyncFn, AsyncFnMut, AsyncFnOnce, CoerceUnsized, ControlFlow, Coroutine, CoroutineState, Deref, + DerefMut, DerefPure, DispatchFromDyn, FromResidual, LegacyReceiver, Residual, Try, }; use core::pin::{Pin, PinCoerceUnsized}; use core::ptr::{self, NonNull, Unique}; @@ -389,6 +389,93 @@ impl Box { pub fn try_new_zeroed() -> Result>, AllocError> { Box::try_new_zeroed_in(Global) } + + /// Maps the value in a box, reusing the allocation if possible. + /// + /// `f` is called on the value in the box, and the result is returned, also boxed. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Box::map(b, f)` instead of `b.map(f)`. This + /// is so that there is no conflict with a method on the inner type. + /// + /// # Examples + /// + /// ``` + /// #![feature(smart_pointer_try_map)] + /// + /// let b = Box::new(7); + /// let new = Box::map(b, |i| i + 7); + /// assert_eq!(*new, 14); + /// ``` + #[unstable(feature = "smart_pointer_try_map", issue = "144419")] + pub fn map(this: Self, f: impl FnOnce(T) -> U) -> Box { + if size_of::() == size_of::() && align_of::().is_multiple_of(align_of::()) { + let ptr = Box::into_raw(this); + unsafe { + ptr.cast::().write(f(ptr.read())); + Box::from_raw(ptr.cast::()) + } + } else { + Box::new(f(*this)) + } + } + + /// Attempts to map the value in a box, reusing the allocation if possible. + /// + /// `f` is called on the value in the box, and if the operation succeeds, the result is + /// returned, also boxed. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Box::try_map(b, f)` instead of `b.try_map(f)`. This + /// is so that there is no conflict with a method on the inner type. + /// + /// # Examples + /// + /// ``` + /// #![feature(smart_pointer_try_map)] + /// + /// let b = Box::new(7); + /// let new = Box::try_map(b, u32::try_from).unwrap(); + /// assert_eq!(*new, 7); + /// ``` + #[unstable(feature = "smart_pointer_try_map", issue = "144419")] + pub fn try_map( + this: Self, + f: impl FnOnce(T) -> R, + ) -> >>::TryType + where + R: Try, + R::Residual: Residual>, + { + if size_of::() == size_of::() + && align_of::().is_multiple_of(align_of::()) + { + let ptr = Box::into_raw(this); + unsafe { + match f(ptr.read()).branch() { + ControlFlow::Continue(c) => { + ptr.cast::().write(c); + >>::TryType::from_output( + Box::from_raw(ptr.cast::()), + ) + } + ControlFlow::Break(b) => { + drop(Box::from_raw(ptr)); + >>::TryType::from_residual(b) + } + } + } + } else { + match f(*this).branch() { + ControlFlow::Continue(c) => { + >>::TryType::from_output(Box::new(c)) + } + ControlFlow::Break(b) => { + >>::TryType::from_residual(b) + } + } + } + } } impl Box { diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 6b6e4df4cba72..3465c09f7dbe5 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -153,6 +153,7 @@ #![feature(trusted_len)] #![feature(trusted_random_access)] #![feature(try_trait_v2)] +#![feature(try_trait_v2_residual)] #![feature(try_with_capacity)] #![feature(tuple_trait)] #![feature(ub_checks)]