From 3c880f2405cbb0b54a20ce192a005b11744f8960 Mon Sep 17 00:00:00 2001 From: Trevor Leibert Date: Tue, 18 Apr 2023 01:04:57 -0400 Subject: [PATCH 1/2] Create try_new function for ThinBox --- library/alloc/src/boxed/thin.rs | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index ad48315fd70cc..b210f3ee572c2 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -69,6 +69,27 @@ impl ThinBox { } } +#[unstable(feature = "thin_box", issue = "92791")] +impl ThinBox { + /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on + /// the stack. Returns an error if allocation fails, instead of aborting. + /// + /// # Examples + /// + /// ``` + /// #![feature(thin_box)] + /// use std::boxed::ThinBox; + /// + /// let five = ThinBox::new(5); + /// ``` + /// + /// [`Metadata`]: core::ptr::Pointee::Metadata + pub fn try_new(value: T) -> Result { + let meta = ptr::metadata(&value); + WithOpaqueHeader::try_new(meta, value).map(|ptr| ThinBox { ptr, _marker: PhantomData }) + } +} + #[unstable(feature = "thin_box", issue = "92791")] impl ThinBox { /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on @@ -179,6 +200,10 @@ impl WithOpaqueHeader { let ptr = WithHeader::new(header, value); Self(ptr.0) } + + fn try_new(header: H, value: T) -> Result { + WithHeader::try_new(header, value).map(|ptr| Self(ptr.0)) + } } impl WithHeader { @@ -226,6 +251,46 @@ impl WithHeader { } } + /// Non-panicking version of `new`. + /// Any error is returned as `Err(core::alloc::AllocError)`. + fn try_new(header: H, value: T) -> Result, core::alloc::AllocError> { + let value_layout = Layout::new::(); + let Ok((layout, value_offset)) = Self::alloc_layout(value_layout) else { + return Err(core::alloc::AllocError); + }; + + unsafe { + // Note: It's UB to pass a layout with a zero size to `alloc::alloc`, so + // we use `layout.dangling()` for this case, which should have a valid + // alignment for both `T` and `H`. + let ptr = if layout.size() == 0 { + // Some paranoia checking, mostly so that the ThinBox tests are + // more able to catch issues. + debug_assert!( + value_offset == 0 && mem::size_of::() == 0 && mem::size_of::() == 0 + ); + layout.dangling() + } else { + let ptr = alloc::alloc(layout); + if ptr.is_null() { + return Err(core::alloc::AllocError); + } + + // Safety: + // - The size is at least `aligned_header_size`. + let ptr = ptr.add(value_offset) as *mut _; + + NonNull::new_unchecked(ptr) + }; + + let result = WithHeader(ptr, PhantomData); + ptr::write(result.header(), header); + ptr::write(result.value().cast(), value); + + Ok(result) + } + } + // Safety: // - Assumes that either `value` can be dereferenced, or is the // `NonNull::dangling()` we use when both `T` and `H` are ZSTs. From ea6944a065bdc64855c221f8e2fd969df4d21c6d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 11 Feb 2024 11:15:21 -0800 Subject: [PATCH 2/2] Address ThinBox::try_new PR review --- library/alloc/src/boxed/thin.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index b210f3ee572c2..727d7143ff77f 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -67,20 +67,19 @@ impl ThinBox { let ptr = WithOpaqueHeader::new(meta, value); ThinBox { ptr, _marker: PhantomData } } -} -#[unstable(feature = "thin_box", issue = "92791")] -impl ThinBox { /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on /// the stack. Returns an error if allocation fails, instead of aborting. /// /// # Examples /// /// ``` + /// #![feature(allocator_api)] /// #![feature(thin_box)] /// use std::boxed::ThinBox; /// - /// let five = ThinBox::new(5); + /// let five = ThinBox::try_new(5)?; + /// # Ok::<(), std::alloc::AllocError>(()) /// ``` /// /// [`Metadata`]: core::ptr::Pointee::Metadata