mirror of https://github.com/rust-lang/rust.git
Auto merge of #80310 - Manishearth:box-try-alloc, r=kennytm
Add fallible Box, Arc, and Rc allocator APIs cc https://github.com/rust-lang/rust/issues/48043 It was suggested in https://github.com/rust-lang/rust/issues/48043#issuecomment-748008486 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`
This commit is contained in:
commit
18d27b2c94
|
@ -153,7 +153,7 @@ use core::pin::Pin;
|
|||
use core::ptr::{self, Unique};
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use crate::alloc::{handle_alloc_error, Allocator, Global, Layout};
|
||||
use crate::alloc::{handle_alloc_error, AllocError, Allocator, Global, Layout};
|
||||
use crate::borrow::Cow;
|
||||
use crate::raw_vec::RawVec;
|
||||
use crate::str::from_boxed_utf8_unchecked;
|
||||
|
@ -241,6 +241,78 @@ impl<T> Box<T> {
|
|||
pub fn pin(x: T) -> Pin<Box<T>> {
|
||||
(box x).into()
|
||||
}
|
||||
|
||||
/// Allocates memory on the heap then places `x` into it,
|
||||
/// returning an error if the allocation fails
|
||||
///
|
||||
/// This doesn't actually allocate if `T` is zero-sized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(allocator_api)]
|
||||
///
|
||||
/// let five = Box::try_new(5)?;
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
#[inline]
|
||||
pub fn try_new(x: T) -> Result<Self, AllocError> {
|
||||
Self::try_new_in(x, Global)
|
||||
}
|
||||
|
||||
/// Constructs a new box with uninitialized contents on the heap,
|
||||
/// returning an error if the allocation fails
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(allocator_api, new_uninit)]
|
||||
///
|
||||
/// let mut five = Box::<u32>::try_new_uninit()?;
|
||||
///
|
||||
/// let five = unsafe {
|
||||
/// // Deferred initialization:
|
||||
/// five.as_mut_ptr().write(5);
|
||||
///
|
||||
/// five.assume_init()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(*five, 5);
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
#[inline]
|
||||
pub fn try_new_uninit() -> Result<Box<mem::MaybeUninit<T>>, AllocError> {
|
||||
Box::try_new_uninit_in(Global)
|
||||
}
|
||||
|
||||
/// Constructs a new `Box` with uninitialized contents, with the memory
|
||||
/// being filled with `0` bytes on the heap
|
||||
///
|
||||
/// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage
|
||||
/// of this method.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(allocator_api, new_uninit)]
|
||||
///
|
||||
/// let zero = Box::<u32>::try_new_zeroed()?;
|
||||
/// let zero = unsafe { zero.assume_init() };
|
||||
///
|
||||
/// assert_eq!(*zero, 0);
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
///
|
||||
/// [zeroed]: mem::MaybeUninit::zeroed
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
#[inline]
|
||||
pub fn try_new_zeroed() -> Result<Box<mem::MaybeUninit<T>>, AllocError> {
|
||||
Box::try_new_zeroed_in(Global)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A: Allocator> Box<T, A> {
|
||||
|
@ -267,6 +339,31 @@ impl<T, A: Allocator> Box<T, A> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Allocates memory in the given allocator then places `x` into it,
|
||||
/// returning an error if the allocation fails
|
||||
///
|
||||
/// This doesn't actually allocate if `T` is zero-sized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(allocator_api)]
|
||||
///
|
||||
/// use std::alloc::System;
|
||||
///
|
||||
/// let five = Box::try_new_in(5, System)?;
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
#[inline]
|
||||
pub fn try_new_in(x: T, alloc: A) -> Result<Self, AllocError> {
|
||||
let mut boxed = Self::try_new_uninit_in(alloc)?;
|
||||
unsafe {
|
||||
boxed.as_mut_ptr().write(x);
|
||||
Ok(boxed.assume_init())
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a new box with uninitialized contents in the provided allocator.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -291,8 +388,37 @@ impl<T, A: Allocator> Box<T, A> {
|
|||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
pub fn new_uninit_in(alloc: A) -> Box<mem::MaybeUninit<T>, A> {
|
||||
let layout = Layout::new::<mem::MaybeUninit<T>>();
|
||||
let ptr = alloc.allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)).cast();
|
||||
unsafe { Box::from_raw_in(ptr.as_ptr(), alloc) }
|
||||
Box::try_new_uninit_in(alloc).unwrap_or_else(|_| handle_alloc_error(layout))
|
||||
}
|
||||
|
||||
/// Constructs a new box with uninitialized contents in the provided allocator,
|
||||
/// returning an error if the allocation fails
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(allocator_api, new_uninit)]
|
||||
///
|
||||
/// use std::alloc::System;
|
||||
///
|
||||
/// let mut five = Box::<u32, _>::try_new_uninit_in(System)?;
|
||||
///
|
||||
/// let five = unsafe {
|
||||
/// // Deferred initialization:
|
||||
/// five.as_mut_ptr().write(5);
|
||||
///
|
||||
/// five.assume_init()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(*five, 5);
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
pub fn try_new_uninit_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError> {
|
||||
let layout = Layout::new::<mem::MaybeUninit<T>>();
|
||||
let ptr = alloc.allocate(layout)?.cast();
|
||||
unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) }
|
||||
}
|
||||
|
||||
/// Constructs a new `Box` with uninitialized contents, with the memory
|
||||
|
@ -319,9 +445,37 @@ impl<T, A: Allocator> Box<T, A> {
|
|||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
pub fn new_zeroed_in(alloc: A) -> Box<mem::MaybeUninit<T>, A> {
|
||||
let layout = Layout::new::<mem::MaybeUninit<T>>();
|
||||
let ptr =
|
||||
alloc.allocate_zeroed(layout).unwrap_or_else(|_| handle_alloc_error(layout)).cast();
|
||||
unsafe { Box::from_raw_in(ptr.as_ptr(), alloc) }
|
||||
Box::try_new_zeroed_in(alloc).unwrap_or_else(|_| handle_alloc_error(layout))
|
||||
}
|
||||
|
||||
/// Constructs a new `Box` with uninitialized contents, with the memory
|
||||
/// being filled with `0` bytes in the provided allocator,
|
||||
/// returning an error if the allocation fails,
|
||||
///
|
||||
/// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage
|
||||
/// of this method.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(allocator_api, new_uninit)]
|
||||
///
|
||||
/// use std::alloc::System;
|
||||
///
|
||||
/// let zero = Box::<u32, _>::try_new_zeroed_in(System)?;
|
||||
/// let zero = unsafe { zero.assume_init() };
|
||||
///
|
||||
/// assert_eq!(*zero, 0);
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
///
|
||||
/// [zeroed]: mem::MaybeUninit::zeroed
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
pub fn try_new_zeroed_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError> {
|
||||
let layout = Layout::new::<mem::MaybeUninit<T>>();
|
||||
let ptr = alloc.allocate_zeroed(layout)?.cast();
|
||||
unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) }
|
||||
}
|
||||
|
||||
/// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement `Unpin`, then
|
||||
|
|
|
@ -453,6 +453,95 @@ impl<T> Rc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Constructs a new `Rc<T>`, returning an error if the allocation fails
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(allocator_api)]
|
||||
/// use std::rc::Rc;
|
||||
///
|
||||
/// let five = Rc::try_new(5);
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
pub fn try_new(value: T) -> Result<Rc<T>, AllocError> {
|
||||
// There is an implicit weak pointer owned by all the strong
|
||||
// pointers, which ensures that the weak destructor never frees
|
||||
// the allocation while the strong destructor is running, even
|
||||
// if the weak pointer is stored inside the strong one.
|
||||
Ok(Self::from_inner(
|
||||
Box::leak(Box::try_new(RcBox { strong: Cell::new(1), weak: Cell::new(1), value })?)
|
||||
.into(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Constructs a new `Rc` with uninitialized contents, returning an error if the allocation fails
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(allocator_api, new_uninit)]
|
||||
/// #![feature(get_mut_unchecked)]
|
||||
///
|
||||
/// use std::rc::Rc;
|
||||
///
|
||||
/// let mut five = Rc::<u32>::try_new_uninit()?;
|
||||
///
|
||||
/// let five = unsafe {
|
||||
/// // Deferred initialization:
|
||||
/// Rc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);
|
||||
///
|
||||
/// five.assume_init()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(*five, 5);
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
pub fn try_new_uninit() -> Result<Rc<mem::MaybeUninit<T>>, AllocError> {
|
||||
unsafe {
|
||||
Ok(Rc::from_ptr(Rc::try_allocate_for_layout(
|
||||
Layout::new::<T>(),
|
||||
|layout| Global.allocate(layout),
|
||||
|mem| mem as *mut RcBox<mem::MaybeUninit<T>>,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a new `Rc` with uninitialized contents, with the memory
|
||||
/// being filled with `0` bytes, returning an error if the allocation fails
|
||||
///
|
||||
/// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and
|
||||
/// incorrect usage of this method.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(allocator_api, new_uninit)]
|
||||
///
|
||||
/// use std::rc::Rc;
|
||||
///
|
||||
/// let zero = Rc::<u32>::try_new_zeroed()?;
|
||||
/// let zero = unsafe { zero.assume_init() };
|
||||
///
|
||||
/// assert_eq!(*zero, 0);
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
///
|
||||
/// [zeroed]: mem::MaybeUninit::zeroed
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
//#[unstable(feature = "new_uninit", issue = "63291")]
|
||||
pub fn try_new_zeroed() -> Result<Rc<mem::MaybeUninit<T>>, AllocError> {
|
||||
unsafe {
|
||||
Ok(Rc::from_ptr(Rc::try_allocate_for_layout(
|
||||
Layout::new::<T>(),
|
||||
|layout| Global.allocate_zeroed(layout),
|
||||
|mem| mem as *mut RcBox<mem::MaybeUninit<T>>,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
/// Constructs a new `Pin<Rc<T>>`. If `T` does not implement `Unpin`, then
|
||||
/// `value` will be pinned in memory and unable to be moved.
|
||||
#[stable(feature = "pin", since = "1.33.0")]
|
||||
|
@ -1018,9 +1107,32 @@ impl<T: ?Sized> Rc<T> {
|
|||
// `&*(ptr as *const RcBox<T>)`, but this created a misaligned
|
||||
// reference (see #54908).
|
||||
let layout = Layout::new::<RcBox<()>>().extend(value_layout).unwrap().0.pad_to_align();
|
||||
unsafe {
|
||||
Rc::try_allocate_for_layout(value_layout, allocate, mem_to_rcbox)
|
||||
.unwrap_or_else(|_| handle_alloc_error(layout))
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates an `RcBox<T>` with sufficient space for
|
||||
/// a possibly-unsized inner value where the value has the layout provided,
|
||||
/// returning an error if allocation fails.
|
||||
///
|
||||
/// The function `mem_to_rcbox` is called with the data pointer
|
||||
/// and must return back a (potentially fat)-pointer for the `RcBox<T>`.
|
||||
#[inline]
|
||||
unsafe fn try_allocate_for_layout(
|
||||
value_layout: Layout,
|
||||
allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
|
||||
mem_to_rcbox: impl FnOnce(*mut u8) -> *mut RcBox<T>,
|
||||
) -> Result<*mut RcBox<T>, AllocError> {
|
||||
// Calculate layout using the given value layout.
|
||||
// Previously, layout was calculated on the expression
|
||||
// `&*(ptr as *const RcBox<T>)`, but this created a misaligned
|
||||
// reference (see #54908).
|
||||
let layout = Layout::new::<RcBox<()>>().extend(value_layout).unwrap().0.pad_to_align();
|
||||
|
||||
// Allocate for the layout.
|
||||
let ptr = allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout));
|
||||
let ptr = allocate(layout)?;
|
||||
|
||||
// Initialize the RcBox
|
||||
let inner = mem_to_rcbox(ptr.as_non_null_ptr().as_ptr());
|
||||
|
@ -1031,7 +1143,7 @@ impl<T: ?Sized> Rc<T> {
|
|||
ptr::write(&mut (*inner).weak, Cell::new(1));
|
||||
}
|
||||
|
||||
inner
|
||||
Ok(inner)
|
||||
}
|
||||
|
||||
/// Allocates an `RcBox<T>` with sufficient space for an unsized inner value
|
||||
|
|
|
@ -478,6 +478,97 @@ impl<T> Arc<T> {
|
|||
unsafe { Pin::new_unchecked(Arc::new(data)) }
|
||||
}
|
||||
|
||||
/// Constructs a new `Arc<T>`, returning an error if allocation fails.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(allocator_api)]
|
||||
/// use std::sync::Arc;
|
||||
///
|
||||
/// let five = Arc::try_new(5)?;
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
#[inline]
|
||||
pub fn try_new(data: T) -> Result<Arc<T>, AllocError> {
|
||||
// Start the weak pointer count as 1 which is the weak pointer that's
|
||||
// held by all the strong pointers (kinda), see std/rc.rs for more info
|
||||
let x: Box<_> = Box::try_new(ArcInner {
|
||||
strong: atomic::AtomicUsize::new(1),
|
||||
weak: atomic::AtomicUsize::new(1),
|
||||
data,
|
||||
})?;
|
||||
Ok(Self::from_inner(Box::leak(x).into()))
|
||||
}
|
||||
|
||||
/// Constructs a new `Arc` with uninitialized contents, returning an error
|
||||
/// if allocation fails.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(new_uninit, allocator_api)]
|
||||
/// #![feature(get_mut_unchecked)]
|
||||
///
|
||||
/// use std::sync::Arc;
|
||||
///
|
||||
/// let mut five = Arc::<u32>::try_new_uninit()?;
|
||||
///
|
||||
/// let five = unsafe {
|
||||
/// // Deferred initialization:
|
||||
/// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);
|
||||
///
|
||||
/// five.assume_init()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(*five, 5);
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
pub fn try_new_uninit() -> Result<Arc<mem::MaybeUninit<T>>, AllocError> {
|
||||
unsafe {
|
||||
Ok(Arc::from_ptr(Arc::try_allocate_for_layout(
|
||||
Layout::new::<T>(),
|
||||
|layout| Global.allocate(layout),
|
||||
|mem| mem as *mut ArcInner<mem::MaybeUninit<T>>,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a new `Arc` with uninitialized contents, with the memory
|
||||
/// being filled with `0` bytes, returning an error if allocation fails.
|
||||
///
|
||||
/// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage
|
||||
/// of this method.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(new_uninit, allocator_api)]
|
||||
///
|
||||
/// use std::sync::Arc;
|
||||
///
|
||||
/// let zero = Arc::<u32>::try_new_zeroed()?;
|
||||
/// let zero = unsafe { zero.assume_init() };
|
||||
///
|
||||
/// assert_eq!(*zero, 0);
|
||||
/// # Ok::<(), std::alloc::AllocError>(())
|
||||
/// ```
|
||||
///
|
||||
/// [zeroed]: mem::MaybeUninit::zeroed
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
// #[unstable(feature = "new_uninit", issue = "63291")]
|
||||
pub fn try_new_zeroed() -> Result<Arc<mem::MaybeUninit<T>>, AllocError> {
|
||||
unsafe {
|
||||
Ok(Arc::from_ptr(Arc::try_allocate_for_layout(
|
||||
Layout::new::<T>(),
|
||||
|layout| Global.allocate_zeroed(layout),
|
||||
|mem| mem as *mut ArcInner<mem::MaybeUninit<T>>,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
/// Returns the inner value, if the `Arc` has exactly one strong reference.
|
||||
///
|
||||
/// Otherwise, an [`Err`] is returned with the same `Arc` that was
|
||||
|
@ -994,8 +1085,30 @@ impl<T: ?Sized> Arc<T> {
|
|||
// `&*(ptr as *const ArcInner<T>)`, but this created a misaligned
|
||||
// reference (see #54908).
|
||||
let layout = Layout::new::<ArcInner<()>>().extend(value_layout).unwrap().0.pad_to_align();
|
||||
unsafe {
|
||||
Arc::try_allocate_for_layout(value_layout, allocate, mem_to_arcinner)
|
||||
.unwrap_or_else(|_| handle_alloc_error(layout))
|
||||
}
|
||||
}
|
||||
|
||||
let ptr = allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout));
|
||||
/// Allocates an `ArcInner<T>` with sufficient space for
|
||||
/// a possibly-unsized inner value where the value has the layout provided,
|
||||
/// returning an error if allocation fails.
|
||||
///
|
||||
/// The function `mem_to_arcinner` is called with the data pointer
|
||||
/// and must return back a (potentially fat)-pointer for the `ArcInner<T>`.
|
||||
unsafe fn try_allocate_for_layout(
|
||||
value_layout: Layout,
|
||||
allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
|
||||
mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner<T>,
|
||||
) -> Result<*mut ArcInner<T>, AllocError> {
|
||||
// Calculate layout using the given value layout.
|
||||
// Previously, layout was calculated on the expression
|
||||
// `&*(ptr as *const ArcInner<T>)`, but this created a misaligned
|
||||
// reference (see #54908).
|
||||
let layout = Layout::new::<ArcInner<()>>().extend(value_layout).unwrap().0.pad_to_align();
|
||||
|
||||
let ptr = allocate(layout)?;
|
||||
|
||||
// Initialize the ArcInner
|
||||
let inner = mem_to_arcinner(ptr.as_non_null_ptr().as_ptr());
|
||||
|
@ -1006,7 +1119,7 @@ impl<T: ?Sized> Arc<T> {
|
|||
ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1));
|
||||
}
|
||||
|
||||
inner
|
||||
Ok(inner)
|
||||
}
|
||||
|
||||
/// Allocates an `ArcInner<T>` with sufficient space for an unsized inner value.
|
||||
|
|
Loading…
Reference in New Issue