Skip to content

Commit

Permalink
Auto merge of rust-lang#101837 - scottmcm:box-array-from-vec, r=m-ou-se
Browse files Browse the repository at this point in the history
Add `Box<[T; N]>: TryFrom<Vec<T>>`

We have `[T; N]: TryFrom<Vec<T>>` (rust-lang#76310) and `Box<[T; N]>: TryFrom<Box<[T]>>`, but not this combination.

`vec.into_boxed_slice().try_into()` isn't quite a replacement for this, as that'll reallocate unnecessarily in the error case.

**Insta-stable, so needs an FCP**

(I tried to make this work with `, A`, but that's disallowed because of `#[fundamental]` rust-lang#29635 (comment))
  • Loading branch information
bors committed Oct 17, 2022
2 parents 9c2797d + 4d3a31c commit 06f049a
Showing 1 changed file with 50 additions and 1 deletion.
51 changes: 50 additions & 1 deletion library/alloc/src/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1620,6 +1620,22 @@ impl<T, const N: usize> From<[T; N]> for Box<[T]> {
}
}

/// Casts a boxed slice to a boxed array.
///
/// # Safety
///
/// `boxed_slice.len()` must be exactly `N`.
unsafe fn boxed_slice_as_array_unchecked<T, A: Allocator, const N: usize>(
boxed_slice: Box<[T], A>,
) -> Box<[T; N], A> {
debug_assert_eq!(boxed_slice.len(), N);

let (ptr, alloc) = Box::into_raw_with_allocator(boxed_slice);
// SAFETY: Pointer and allocator came from an existing box,
// and our safety condition requires that the length is exactly `N`
unsafe { Box::from_raw_in(ptr as *mut [T; N], alloc) }
}

#[stable(feature = "boxed_slice_try_from", since = "1.43.0")]
impl<T, const N: usize> TryFrom<Box<[T]>> for Box<[T; N]> {
type Error = Box<[T]>;
Expand All @@ -1635,13 +1651,46 @@ impl<T, const N: usize> TryFrom<Box<[T]>> for Box<[T; N]> {
/// `boxed_slice.len()` does not equal `N`.
fn try_from(boxed_slice: Box<[T]>) -> Result<Self, Self::Error> {
if boxed_slice.len() == N {
Ok(unsafe { Box::from_raw(Box::into_raw(boxed_slice) as *mut [T; N]) })
Ok(unsafe { boxed_slice_as_array_unchecked(boxed_slice) })
} else {
Err(boxed_slice)
}
}
}

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "boxed_array_try_from_vec", since = "CURRENT_RUSTC_VERSION")]
impl<T, const N: usize> TryFrom<Vec<T>> for Box<[T; N]> {
type Error = Vec<T>;

/// Attempts to convert a `Vec<T>` into a `Box<[T; N]>`.
///
/// Like [`Vec::into_boxed_slice`], this is in-place if `vec.capacity() == N`,
/// but will require a reallocation otherwise.
///
/// # Errors
///
/// Returns the original `Vec<T>` in the `Err` variant if
/// `boxed_slice.len()` does not equal `N`.
///
/// # Examples
///
/// This can be used with [`vec!`] to create an array on the heap:
///
/// ```
/// let state: Box<[f32; 100]> = vec![1.0; 100].try_into().unwrap();
/// assert_eq!(state.len(), 100);
/// ```
fn try_from(vec: Vec<T>) -> Result<Self, Self::Error> {
if vec.len() == N {
let boxed_slice = vec.into_boxed_slice();
Ok(unsafe { boxed_slice_as_array_unchecked(boxed_slice) })
} else {
Err(vec)
}
}
}

impl<A: Allocator> Box<dyn Any, A> {
/// Attempt to downcast the box to a concrete type.
///
Expand Down

0 comments on commit 06f049a

Please sign in to comment.