Skip to content

Commit 2ee0f1d

Browse files
committed
add Box::(try_)map
1 parent 55b9b4d commit 2ee0f1d

File tree

7 files changed

+92
-15
lines changed

7 files changed

+92
-15
lines changed

library/alloc/src/boxed.rs

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,10 @@ use core::fmt;
192192
use core::future::Future;
193193
use core::hash::{Hash, Hasher};
194194
use core::marker::{Tuple, Unsize};
195-
use core::mem::{self, SizedTypeProperties};
195+
use core::mem::{self, MaybeUninit, SizedTypeProperties};
196196
use core::ops::{
197197
AsyncFn, AsyncFnMut, AsyncFnOnce, CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut,
198-
DerefPure, DispatchFromDyn, LegacyReceiver,
198+
DerefPure, DispatchFromDyn, LegacyReceiver, Residual, Try,
199199
};
200200
use core::pin::{Pin, PinCoerceUnsized};
201201
use core::ptr::{self, NonNull, Unique};
@@ -389,6 +389,84 @@ 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+
#[cfg(not(no_global_oom_handling))]
411+
#[unstable(feature = "smart_pointer_try_map", issue = "144419")]
412+
pub fn map<U>(this: Self, f: impl FnOnce(T) -> U) -> Box<U> {
413+
if size_of::<T>() == size_of::<U>() && align_of::<T>() == align_of::<U>() {
414+
let (allocation, value) = unsafe {
415+
let ptr = Box::into_raw(this);
416+
let value = ptr.read();
417+
let allocation = Box::from_raw(ptr.cast::<MaybeUninit<U>>());
418+
(allocation, value)
419+
};
420+
421+
Box::write(allocation, f(value))
422+
} else {
423+
Box::new(f(*this))
424+
}
425+
}
426+
427+
/// Attempts to map the value in a box, reusing the allocation if possible.
428+
///
429+
/// `f` is called on the value in the box, and if the operation succeeds, the result is
430+
/// returned, also boxed.
431+
///
432+
/// Note: this is an associated function, which means that you have
433+
/// to call it as `Box::try_map(b, f)` instead of `b.try_map(f)`. This
434+
/// is so that there is no conflict with a method on the inner type.
435+
///
436+
/// # Examples
437+
///
438+
/// ```
439+
/// #![feature(smart_pointer_try_map)]
440+
///
441+
/// let b = Box::new(7);
442+
/// let new = Box::try_map(b, u32::try_from).unwrap();
443+
/// assert_eq!(*new, 7);
444+
/// ```
445+
#[cfg(not(no_global_oom_handling))]
446+
#[unstable(feature = "smart_pointer_try_map", issue = "144419")]
447+
pub fn try_map<R>(
448+
this: Self,
449+
f: impl FnOnce(T) -> R,
450+
) -> <R::Residual as Residual<Box<R::Output>>>::TryType
451+
where
452+
R: Try,
453+
R::Residual: Residual<Box<R::Output>>,
454+
{
455+
if size_of::<T>() == size_of::<R::Output>() && align_of::<T>() == align_of::<R::Output>() {
456+
let (allocation, value) = unsafe {
457+
let ptr = Box::into_raw(this);
458+
let value = ptr.read();
459+
let allocation = Box::from_raw(ptr.cast::<MaybeUninit<R::Output>>());
460+
(allocation, value)
461+
};
462+
<R::Residual as Residual<Box<R::Output>>>::TryType::from_output(Box::write(
463+
allocation,
464+
f(value)?,
465+
))
466+
} else {
467+
<R::Residual as Residual<Box<R::Output>>>::TryType::from_output(Box::new(f(*this)?))
468+
}
469+
}
392470
}
393471

394472
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)]

tests/ui/parallel-rustc/ty-variance-issue-124423.stderr

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,9 @@ note: if you're trying to build a new `Box<_, _>` consider using one of the foll
278278
Box::<T>::new_uninit
279279
Box::<T>::new_zeroed
280280
Box::<T>::try_new
281-
and 22 others
282281
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
283282

284283
error: aborting due to 30 previous errors
285284

286285
Some errors have detailed explanations: E0121, E0224, E0261, E0412, E0599.
287-
For more information about an error, try `rustc --explain E0121`.
286+
For more information about an error, try `rustc --explain E0121`.

tests/ui/parallel-rustc/ty-variance-issue-127971.stderr

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,9 @@ note: if you're trying to build a new `Box<_, _>` consider using one of the foll
104104
Box::<T>::new_uninit
105105
Box::<T>::new_zeroed
106106
Box::<T>::try_new
107-
and 22 others
108107
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
109108

110109
error: aborting due to 11 previous errors
111110

112111
Some errors have detailed explanations: E0121, E0224, E0261, E0599.
113-
For more information about an error, try `rustc --explain E0121`.
112+
For more information about an error, try `rustc --explain E0121`.

tests/ui/privacy/suggest-box-new.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ LL - x: (),
6363
LL - })),
6464
LL + wtf: Some(Box::new_in(_, _)),
6565
|
66-
= and 12 other candidates
66+
= and 13 other candidates
6767
help: consider using the `Default` trait
6868
|
6969
LL - wtf: Some(Box(U {
@@ -118,7 +118,7 @@ LL + let _ = Box::new_zeroed();
118118
LL - let _ = Box {};
119119
LL + let _ = Box::new_in(_, _);
120120
|
121-
= and 13 other candidates
121+
= and 14 other candidates
122122
help: consider using the `Default` trait
123123
|
124124
LL - let _ = Box {};
@@ -141,12 +141,12 @@ LL - let _ = Box::<i32> {};
141141
LL + let _ = Box::<i32>::new_in(_, _);
142142
|
143143
LL - let _ = Box::<i32> {};
144-
LL + let _ = Box::<i32>::into_inner(_);
144+
LL + let _ = Box::<i32>::map(_, _);
145145
|
146146
LL - let _ = Box::<i32> {};
147-
LL + let _ = Box::<i32>::write(_, _);
147+
LL + let _ = Box::<i32>::into_inner(_);
148148
|
149-
= and 4 other candidates
149+
= and 5 other candidates
150150
help: consider using the `Default` trait
151151
|
152152
LL - let _ = Box::<i32> {};

tests/ui/suggestions/multi-suggestion.ascii.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ LL - x: (),
6363
LL - })),
6464
LL + wtf: Some(Box::new_in(_, _)),
6565
|
66-
= and 12 other candidates
66+
= and 13 other candidates
6767
help: consider using the `Default` trait
6868
|
6969
LL - wtf: Some(Box(U {
@@ -118,7 +118,7 @@ LL + let _ = Box::new_zeroed();
118118
LL - let _ = Box {};
119119
LL + let _ = Box::new_in(_, _);
120120
|
121-
= and 13 other candidates
121+
= and 14 other candidates
122122
help: consider using the `Default` trait
123123
|
124124
LL - let _ = Box {};

tests/ui/suggestions/multi-suggestion.unicode.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ LL - x: (),
6363
LL - })),
6464
LL + wtf: Some(Box::new_in(_, _)),
6565
66-
╰ and 12 other candidates
66+
╰ and 13 other candidates
6767
help: consider using the `Default` trait
6868
╭╴
6969
LL - wtf: Some(Box(U {
@@ -118,7 +118,7 @@ LL + let _ = Box::new_zeroed();
118118
LL - let _ = Box {};
119119
LL + let _ = Box::new_in(_, _);
120120
121-
╰ and 13 other candidates
121+
╰ and 14 other candidates
122122
help: consider using the `Default` trait
123123
╭╴
124124
LL - let _ = Box {};

0 commit comments

Comments
 (0)