Skip to content

Commit 18d27b2

Browse files
committed
Auto merge of #80310 - Manishearth:box-try-alloc, r=kennytm
Add fallible Box, Arc, and Rc allocator APIs cc #48043 It was suggested in #48043 (comment) that `Box::try_*` follows the spirit of RFC 2116. This PR is an attempt to add the relevant APIs, tied to the same feature gate. Happy to make any changes or turn this into an RFC if necessary. cc `@rust-lang/wg-allocators`
2 parents e1fc9ff + 375e7c5 commit 18d27b2

File tree

3 files changed

+389
-10
lines changed

3 files changed

+389
-10
lines changed

library/alloc/src/boxed.rs

+160-6
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ use core::pin::Pin;
153153
use core::ptr::{self, Unique};
154154
use core::task::{Context, Poll};
155155

156-
use crate::alloc::{handle_alloc_error, Allocator, Global, Layout};
156+
use crate::alloc::{handle_alloc_error, AllocError, Allocator, Global, Layout};
157157
use crate::borrow::Cow;
158158
use crate::raw_vec::RawVec;
159159
use crate::str::from_boxed_utf8_unchecked;
@@ -241,6 +241,78 @@ impl<T> Box<T> {
241241
pub fn pin(x: T) -> Pin<Box<T>> {
242242
(box x).into()
243243
}
244+
245+
/// Allocates memory on the heap then places `x` into it,
246+
/// returning an error if the allocation fails
247+
///
248+
/// This doesn't actually allocate if `T` is zero-sized.
249+
///
250+
/// # Examples
251+
///
252+
/// ```
253+
/// #![feature(allocator_api)]
254+
///
255+
/// let five = Box::try_new(5)?;
256+
/// # Ok::<(), std::alloc::AllocError>(())
257+
/// ```
258+
#[unstable(feature = "allocator_api", issue = "32838")]
259+
#[inline]
260+
pub fn try_new(x: T) -> Result<Self, AllocError> {
261+
Self::try_new_in(x, Global)
262+
}
263+
264+
/// Constructs a new box with uninitialized contents on the heap,
265+
/// returning an error if the allocation fails
266+
///
267+
/// # Examples
268+
///
269+
/// ```
270+
/// #![feature(allocator_api, new_uninit)]
271+
///
272+
/// let mut five = Box::<u32>::try_new_uninit()?;
273+
///
274+
/// let five = unsafe {
275+
/// // Deferred initialization:
276+
/// five.as_mut_ptr().write(5);
277+
///
278+
/// five.assume_init()
279+
/// };
280+
///
281+
/// assert_eq!(*five, 5);
282+
/// # Ok::<(), std::alloc::AllocError>(())
283+
/// ```
284+
#[unstable(feature = "allocator_api", issue = "32838")]
285+
// #[unstable(feature = "new_uninit", issue = "63291")]
286+
#[inline]
287+
pub fn try_new_uninit() -> Result<Box<mem::MaybeUninit<T>>, AllocError> {
288+
Box::try_new_uninit_in(Global)
289+
}
290+
291+
/// Constructs a new `Box` with uninitialized contents, with the memory
292+
/// being filled with `0` bytes on the heap
293+
///
294+
/// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage
295+
/// of this method.
296+
///
297+
/// # Examples
298+
///
299+
/// ```
300+
/// #![feature(allocator_api, new_uninit)]
301+
///
302+
/// let zero = Box::<u32>::try_new_zeroed()?;
303+
/// let zero = unsafe { zero.assume_init() };
304+
///
305+
/// assert_eq!(*zero, 0);
306+
/// # Ok::<(), std::alloc::AllocError>(())
307+
/// ```
308+
///
309+
/// [zeroed]: mem::MaybeUninit::zeroed
310+
#[unstable(feature = "allocator_api", issue = "32838")]
311+
// #[unstable(feature = "new_uninit", issue = "63291")]
312+
#[inline]
313+
pub fn try_new_zeroed() -> Result<Box<mem::MaybeUninit<T>>, AllocError> {
314+
Box::try_new_zeroed_in(Global)
315+
}
244316
}
245317

246318
impl<T, A: Allocator> Box<T, A> {
@@ -267,6 +339,31 @@ impl<T, A: Allocator> Box<T, A> {
267339
}
268340
}
269341

342+
/// Allocates memory in the given allocator then places `x` into it,
343+
/// returning an error if the allocation fails
344+
///
345+
/// This doesn't actually allocate if `T` is zero-sized.
346+
///
347+
/// # Examples
348+
///
349+
/// ```
350+
/// #![feature(allocator_api)]
351+
///
352+
/// use std::alloc::System;
353+
///
354+
/// let five = Box::try_new_in(5, System)?;
355+
/// # Ok::<(), std::alloc::AllocError>(())
356+
/// ```
357+
#[unstable(feature = "allocator_api", issue = "32838")]
358+
#[inline]
359+
pub fn try_new_in(x: T, alloc: A) -> Result<Self, AllocError> {
360+
let mut boxed = Self::try_new_uninit_in(alloc)?;
361+
unsafe {
362+
boxed.as_mut_ptr().write(x);
363+
Ok(boxed.assume_init())
364+
}
365+
}
366+
270367
/// Constructs a new box with uninitialized contents in the provided allocator.
271368
///
272369
/// # Examples
@@ -291,8 +388,37 @@ impl<T, A: Allocator> Box<T, A> {
291388
// #[unstable(feature = "new_uninit", issue = "63291")]
292389
pub fn new_uninit_in(alloc: A) -> Box<mem::MaybeUninit<T>, A> {
293390
let layout = Layout::new::<mem::MaybeUninit<T>>();
294-
let ptr = alloc.allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)).cast();
295-
unsafe { Box::from_raw_in(ptr.as_ptr(), alloc) }
391+
Box::try_new_uninit_in(alloc).unwrap_or_else(|_| handle_alloc_error(layout))
392+
}
393+
394+
/// Constructs a new box with uninitialized contents in the provided allocator,
395+
/// returning an error if the allocation fails
396+
///
397+
/// # Examples
398+
///
399+
/// ```
400+
/// #![feature(allocator_api, new_uninit)]
401+
///
402+
/// use std::alloc::System;
403+
///
404+
/// let mut five = Box::<u32, _>::try_new_uninit_in(System)?;
405+
///
406+
/// let five = unsafe {
407+
/// // Deferred initialization:
408+
/// five.as_mut_ptr().write(5);
409+
///
410+
/// five.assume_init()
411+
/// };
412+
///
413+
/// assert_eq!(*five, 5);
414+
/// # Ok::<(), std::alloc::AllocError>(())
415+
/// ```
416+
#[unstable(feature = "allocator_api", issue = "32838")]
417+
// #[unstable(feature = "new_uninit", issue = "63291")]
418+
pub fn try_new_uninit_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError> {
419+
let layout = Layout::new::<mem::MaybeUninit<T>>();
420+
let ptr = alloc.allocate(layout)?.cast();
421+
unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) }
296422
}
297423

298424
/// Constructs a new `Box` with uninitialized contents, with the memory
@@ -319,9 +445,37 @@ impl<T, A: Allocator> Box<T, A> {
319445
// #[unstable(feature = "new_uninit", issue = "63291")]
320446
pub fn new_zeroed_in(alloc: A) -> Box<mem::MaybeUninit<T>, A> {
321447
let layout = Layout::new::<mem::MaybeUninit<T>>();
322-
let ptr =
323-
alloc.allocate_zeroed(layout).unwrap_or_else(|_| handle_alloc_error(layout)).cast();
324-
unsafe { Box::from_raw_in(ptr.as_ptr(), alloc) }
448+
Box::try_new_zeroed_in(alloc).unwrap_or_else(|_| handle_alloc_error(layout))
449+
}
450+
451+
/// Constructs a new `Box` with uninitialized contents, with the memory
452+
/// being filled with `0` bytes in the provided allocator,
453+
/// returning an error if the allocation fails,
454+
///
455+
/// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage
456+
/// of this method.
457+
///
458+
/// # Examples
459+
///
460+
/// ```
461+
/// #![feature(allocator_api, new_uninit)]
462+
///
463+
/// use std::alloc::System;
464+
///
465+
/// let zero = Box::<u32, _>::try_new_zeroed_in(System)?;
466+
/// let zero = unsafe { zero.assume_init() };
467+
///
468+
/// assert_eq!(*zero, 0);
469+
/// # Ok::<(), std::alloc::AllocError>(())
470+
/// ```
471+
///
472+
/// [zeroed]: mem::MaybeUninit::zeroed
473+
#[unstable(feature = "allocator_api", issue = "32838")]
474+
// #[unstable(feature = "new_uninit", issue = "63291")]
475+
pub fn try_new_zeroed_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError> {
476+
let layout = Layout::new::<mem::MaybeUninit<T>>();
477+
let ptr = alloc.allocate_zeroed(layout)?.cast();
478+
unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) }
325479
}
326480

327481
/// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement `Unpin`, then

library/alloc/src/rc.rs

+114-2
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,95 @@ impl<T> Rc<T> {
453453
}
454454
}
455455

456+
/// Constructs a new `Rc<T>`, returning an error if the allocation fails
457+
///
458+
/// # Examples
459+
///
460+
/// ```
461+
/// #![feature(allocator_api)]
462+
/// use std::rc::Rc;
463+
///
464+
/// let five = Rc::try_new(5);
465+
/// # Ok::<(), std::alloc::AllocError>(())
466+
/// ```
467+
#[unstable(feature = "allocator_api", issue = "32838")]
468+
pub fn try_new(value: T) -> Result<Rc<T>, AllocError> {
469+
// There is an implicit weak pointer owned by all the strong
470+
// pointers, which ensures that the weak destructor never frees
471+
// the allocation while the strong destructor is running, even
472+
// if the weak pointer is stored inside the strong one.
473+
Ok(Self::from_inner(
474+
Box::leak(Box::try_new(RcBox { strong: Cell::new(1), weak: Cell::new(1), value })?)
475+
.into(),
476+
))
477+
}
478+
479+
/// Constructs a new `Rc` with uninitialized contents, returning an error if the allocation fails
480+
///
481+
/// # Examples
482+
///
483+
/// ```
484+
/// #![feature(allocator_api, new_uninit)]
485+
/// #![feature(get_mut_unchecked)]
486+
///
487+
/// use std::rc::Rc;
488+
///
489+
/// let mut five = Rc::<u32>::try_new_uninit()?;
490+
///
491+
/// let five = unsafe {
492+
/// // Deferred initialization:
493+
/// Rc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);
494+
///
495+
/// five.assume_init()
496+
/// };
497+
///
498+
/// assert_eq!(*five, 5);
499+
/// # Ok::<(), std::alloc::AllocError>(())
500+
/// ```
501+
#[unstable(feature = "allocator_api", issue = "32838")]
502+
// #[unstable(feature = "new_uninit", issue = "63291")]
503+
pub fn try_new_uninit() -> Result<Rc<mem::MaybeUninit<T>>, AllocError> {
504+
unsafe {
505+
Ok(Rc::from_ptr(Rc::try_allocate_for_layout(
506+
Layout::new::<T>(),
507+
|layout| Global.allocate(layout),
508+
|mem| mem as *mut RcBox<mem::MaybeUninit<T>>,
509+
)?))
510+
}
511+
}
512+
513+
/// Constructs a new `Rc` with uninitialized contents, with the memory
514+
/// being filled with `0` bytes, returning an error if the allocation fails
515+
///
516+
/// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and
517+
/// incorrect usage of this method.
518+
///
519+
/// # Examples
520+
///
521+
/// ```
522+
/// #![feature(allocator_api, new_uninit)]
523+
///
524+
/// use std::rc::Rc;
525+
///
526+
/// let zero = Rc::<u32>::try_new_zeroed()?;
527+
/// let zero = unsafe { zero.assume_init() };
528+
///
529+
/// assert_eq!(*zero, 0);
530+
/// # Ok::<(), std::alloc::AllocError>(())
531+
/// ```
532+
///
533+
/// [zeroed]: mem::MaybeUninit::zeroed
534+
#[unstable(feature = "allocator_api", issue = "32838")]
535+
//#[unstable(feature = "new_uninit", issue = "63291")]
536+
pub fn try_new_zeroed() -> Result<Rc<mem::MaybeUninit<T>>, AllocError> {
537+
unsafe {
538+
Ok(Rc::from_ptr(Rc::try_allocate_for_layout(
539+
Layout::new::<T>(),
540+
|layout| Global.allocate_zeroed(layout),
541+
|mem| mem as *mut RcBox<mem::MaybeUninit<T>>,
542+
)?))
543+
}
544+
}
456545
/// Constructs a new `Pin<Rc<T>>`. If `T` does not implement `Unpin`, then
457546
/// `value` will be pinned in memory and unable to be moved.
458547
#[stable(feature = "pin", since = "1.33.0")]
@@ -1018,9 +1107,32 @@ impl<T: ?Sized> Rc<T> {
10181107
// `&*(ptr as *const RcBox<T>)`, but this created a misaligned
10191108
// reference (see #54908).
10201109
let layout = Layout::new::<RcBox<()>>().extend(value_layout).unwrap().0.pad_to_align();
1110+
unsafe {
1111+
Rc::try_allocate_for_layout(value_layout, allocate, mem_to_rcbox)
1112+
.unwrap_or_else(|_| handle_alloc_error(layout))
1113+
}
1114+
}
1115+
1116+
/// Allocates an `RcBox<T>` with sufficient space for
1117+
/// a possibly-unsized inner value where the value has the layout provided,
1118+
/// returning an error if allocation fails.
1119+
///
1120+
/// The function `mem_to_rcbox` is called with the data pointer
1121+
/// and must return back a (potentially fat)-pointer for the `RcBox<T>`.
1122+
#[inline]
1123+
unsafe fn try_allocate_for_layout(
1124+
value_layout: Layout,
1125+
allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
1126+
mem_to_rcbox: impl FnOnce(*mut u8) -> *mut RcBox<T>,
1127+
) -> Result<*mut RcBox<T>, AllocError> {
1128+
// Calculate layout using the given value layout.
1129+
// Previously, layout was calculated on the expression
1130+
// `&*(ptr as *const RcBox<T>)`, but this created a misaligned
1131+
// reference (see #54908).
1132+
let layout = Layout::new::<RcBox<()>>().extend(value_layout).unwrap().0.pad_to_align();
10211133

10221134
// Allocate for the layout.
1023-
let ptr = allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout));
1135+
let ptr = allocate(layout)?;
10241136

10251137
// Initialize the RcBox
10261138
let inner = mem_to_rcbox(ptr.as_non_null_ptr().as_ptr());
@@ -1031,7 +1143,7 @@ impl<T: ?Sized> Rc<T> {
10311143
ptr::write(&mut (*inner).weak, Cell::new(1));
10321144
}
10331145

1034-
inner
1146+
Ok(inner)
10351147
}
10361148

10371149
/// Allocates an `RcBox<T>` with sufficient space for an unsized inner value

0 commit comments

Comments
 (0)