Skip to content

Commit 251a09e

Browse files
authored
Rollup merge of rust-lang#110483 - tleibert:thin-box-try-new, r=dtolnay
Create try_new function for ThinBox The `allocator_api` feature has proven very useful in my work in the FreeBSD kernel. I've found a few places where a `ThinBox` rust-lang#92791 would be useful, but it must be able to be fallibly allocated for it to be used in the kernel. This PR proposes a change to add such a constructor for ThinBox. ACP: rust-lang/libs-team#213
2 parents 1a648b3 + ea6944a commit 251a09e

File tree

1 file changed

+64
-0
lines changed

1 file changed

+64
-0
lines changed

library/alloc/src/boxed/thin.rs

+64
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,26 @@ impl<T> ThinBox<T> {
6767
let ptr = WithOpaqueHeader::new(meta, value);
6868
ThinBox { ptr, _marker: PhantomData }
6969
}
70+
71+
/// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on
72+
/// the stack. Returns an error if allocation fails, instead of aborting.
73+
///
74+
/// # Examples
75+
///
76+
/// ```
77+
/// #![feature(allocator_api)]
78+
/// #![feature(thin_box)]
79+
/// use std::boxed::ThinBox;
80+
///
81+
/// let five = ThinBox::try_new(5)?;
82+
/// # Ok::<(), std::alloc::AllocError>(())
83+
/// ```
84+
///
85+
/// [`Metadata`]: core::ptr::Pointee::Metadata
86+
pub fn try_new(value: T) -> Result<Self, core::alloc::AllocError> {
87+
let meta = ptr::metadata(&value);
88+
WithOpaqueHeader::try_new(meta, value).map(|ptr| ThinBox { ptr, _marker: PhantomData })
89+
}
7090
}
7191

7292
#[unstable(feature = "thin_box", issue = "92791")]
@@ -179,6 +199,10 @@ impl WithOpaqueHeader {
179199
let ptr = WithHeader::new(header, value);
180200
Self(ptr.0)
181201
}
202+
203+
fn try_new<H, T>(header: H, value: T) -> Result<Self, core::alloc::AllocError> {
204+
WithHeader::try_new(header, value).map(|ptr| Self(ptr.0))
205+
}
182206
}
183207

184208
impl<H> WithHeader<H> {
@@ -224,6 +248,46 @@ impl<H> WithHeader<H> {
224248
}
225249
}
226250

251+
/// Non-panicking version of `new`.
252+
/// Any error is returned as `Err(core::alloc::AllocError)`.
253+
fn try_new<T>(header: H, value: T) -> Result<WithHeader<H>, core::alloc::AllocError> {
254+
let value_layout = Layout::new::<T>();
255+
let Ok((layout, value_offset)) = Self::alloc_layout(value_layout) else {
256+
return Err(core::alloc::AllocError);
257+
};
258+
259+
unsafe {
260+
// Note: It's UB to pass a layout with a zero size to `alloc::alloc`, so
261+
// we use `layout.dangling()` for this case, which should have a valid
262+
// alignment for both `T` and `H`.
263+
let ptr = if layout.size() == 0 {
264+
// Some paranoia checking, mostly so that the ThinBox tests are
265+
// more able to catch issues.
266+
debug_assert!(
267+
value_offset == 0 && mem::size_of::<T>() == 0 && mem::size_of::<H>() == 0
268+
);
269+
layout.dangling()
270+
} else {
271+
let ptr = alloc::alloc(layout);
272+
if ptr.is_null() {
273+
return Err(core::alloc::AllocError);
274+
}
275+
276+
// Safety:
277+
// - The size is at least `aligned_header_size`.
278+
let ptr = ptr.add(value_offset) as *mut _;
279+
280+
NonNull::new_unchecked(ptr)
281+
};
282+
283+
let result = WithHeader(ptr, PhantomData);
284+
ptr::write(result.header(), header);
285+
ptr::write(result.value().cast(), value);
286+
287+
Ok(result)
288+
}
289+
}
290+
227291
// Safety:
228292
// - Assumes that either `value` can be dereferenced, or is the
229293
// `NonNull::dangling()` we use when both `T` and `H` are ZSTs.

0 commit comments

Comments
 (0)