Skip to content

Commit 64b65c1

Browse files
committed
add Box::(try_)map
1 parent 94722ca commit 64b65c1

File tree

2 files changed

+110
-2
lines changed

2 files changed

+110
-2
lines changed

library/alloc/src/boxed.rs

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,8 @@ use core::hash::{Hash, Hasher};
194194
use core::marker::{Tuple, Unsize};
195195
use core::mem::{self, SizedTypeProperties};
196196
use core::ops::{
197-
AsyncFn, AsyncFnMut, AsyncFnOnce, CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut,
198-
DerefPure, DispatchFromDyn, LegacyReceiver,
197+
AsyncFn, AsyncFnMut, AsyncFnOnce, CoerceUnsized, ControlFlow, Coroutine, CoroutineState, Deref,
198+
DerefMut, DerefPure, DispatchFromDyn, FromResidual, LegacyReceiver, Residual, Try,
199199
};
200200
use core::pin::{Pin, PinCoerceUnsized};
201201
use core::ptr::{self, NonNull, Unique};
@@ -389,6 +389,113 @@ impl<T> Box<T> {
389389
pub fn try_new_zeroed() -> Result<Box<mem::MaybeUninit<T>>, AllocError> {
390390
Box::try_new_zeroed_in(Global)
391391
}
392+
393+
/// Maps the value in a box, reusing the allocation if possible.
394+
///
395+
/// `f` is called on the value in the box, and the result is returned, also boxed.
396+
///
397+
/// Note: this is an associated function, which means that you have
398+
/// to call it as `Box::map(b, f)` instead of `b.map(f)`. This
399+
/// is so that there is no conflict with a method on the inner type.
400+
///
401+
/// # Examples
402+
///
403+
/// ```
404+
/// #![feature(smart_pointer_try_map)]
405+
///
406+
/// let b = Box::new(7);
407+
/// let new = Box::map(b, |i| i + 7);
408+
/// assert_eq!(*new, 14);
409+
/// ```
410+
#[unstable(feature = "smart_pointer_try_map", issue = "144419")]
411+
pub fn map<U>(this: Self, f: impl FnOnce(T) -> U) -> Box<U> {
412+
struct Guard<T>(*mut T);
413+
414+
impl<T> Drop for Guard<T> {
415+
fn drop(&mut self) {
416+
let _box = unsafe { Box::from_raw(self.0) };
417+
}
418+
}
419+
420+
if size_of::<T>() == size_of::<U>() && align_of::<T>() == align_of::<U>() {
421+
let guard = Guard(Box::into_raw(this));
422+
unsafe {
423+
let new = f(guard.0.read());
424+
let ptr = guard.0;
425+
mem::forget(guard);
426+
ptr.cast::<U>().write(new);
427+
Box::from_raw(ptr.cast::<U>())
428+
}
429+
} else {
430+
Box::new(f(*this))
431+
}
432+
}
433+
434+
/// Attempts to map the value in a box, reusing the allocation if possible.
435+
///
436+
/// `f` is called on the value in the box, and if the operation succeeds, the result is
437+
/// returned, also boxed.
438+
///
439+
/// Note: this is an associated function, which means that you have
440+
/// to call it as `Box::try_map(b, f)` instead of `b.try_map(f)`. This
441+
/// is so that there is no conflict with a method on the inner type.
442+
///
443+
/// # Examples
444+
///
445+
/// ```
446+
/// #![feature(smart_pointer_try_map)]
447+
///
448+
/// let b = Box::new(7);
449+
/// let new = Box::try_map(b, u32::try_from).unwrap();
450+
/// assert_eq!(*new, 7);
451+
/// ```
452+
#[unstable(feature = "smart_pointer_try_map", issue = "144419")]
453+
pub fn try_map<R>(
454+
this: Self,
455+
f: impl FnOnce(T) -> R,
456+
) -> <R::Residual as Residual<Box<R::Output>>>::TryType
457+
where
458+
R: Try,
459+
R::Residual: Residual<Box<R::Output>>,
460+
{
461+
struct Guard<T>(*mut T);
462+
463+
impl<T> Drop for Guard<T> {
464+
fn drop(&mut self) {
465+
let _box = unsafe { Box::from_raw(self.0) };
466+
}
467+
}
468+
469+
if size_of::<T>() == size_of::<R::Output>() && align_of::<T>() == align_of::<R::Output>() {
470+
let guard = Guard(Box::into_raw(this));
471+
unsafe {
472+
let new = f(guard.0.read());
473+
let ptr = guard.0;
474+
mem::forget(guard);
475+
match new.branch() {
476+
ControlFlow::Continue(c) => {
477+
ptr.cast::<R::Output>().write(c);
478+
<R::Residual as Residual<Box<R::Output>>>::TryType::from_output(
479+
Box::from_raw(ptr.cast::<R::Output>()),
480+
)
481+
}
482+
ControlFlow::Break(b) => {
483+
drop(Box::from_raw(ptr));
484+
<R::Residual as Residual<Box<R::Output>>>::TryType::from_residual(b)
485+
}
486+
}
487+
}
488+
} else {
489+
match f(*this).branch() {
490+
ControlFlow::Continue(c) => {
491+
<R::Residual as Residual<Box<R::Output>>>::TryType::from_output(Box::new(c))
492+
}
493+
ControlFlow::Break(b) => {
494+
<R::Residual as Residual<Box<R::Output>>>::TryType::from_residual(b)
495+
}
496+
}
497+
}
498+
}
392499
}
393500

394501
impl<T, A: Allocator> Box<T, A> {

library/alloc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153
#![feature(trusted_len)]
154154
#![feature(trusted_random_access)]
155155
#![feature(try_trait_v2)]
156+
#![feature(try_trait_v2_residual)]
156157
#![feature(try_with_capacity)]
157158
#![feature(tuple_trait)]
158159
#![feature(ub_checks)]

0 commit comments

Comments
 (0)