Skip to content

Commit

Permalink
hybrid-array: make ArraySize an unsafe trait
Browse files Browse the repository at this point in the history
It's used when checking the lengths of slices match an array size prior
to using a pointer cast to convert types.

If someone were to make their own `typenum::Unsigned` type and impl
`ArraySize` for it with an `ArrayType` whose size does not match
`Unsigned::USIZE`, that would be UB.

Really `ArraySize` is not intended for downstream crates to impl anyway,
but making it an `unsafe trait` at least captures the UB potential.

Additionally this adds more debug checks to `check_slice_length` to
ensure that if there is a length mismatch, it's at least caught in debug
builds.
  • Loading branch information
tarcieri committed May 31, 2023
1 parent 14b1e13 commit beb2b92
Showing 1 changed file with 19 additions and 3 deletions.
22 changes: 19 additions & 3 deletions hybrid-array/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,10 +304,16 @@ where
}

/// Generate a [`TryFromSliceError`] if the slice doesn't match the given length.
fn check_slice_length<T, U: Unsigned>(slice: &[T]) -> Result<(), TryFromSliceError> {
#[cfg_attr(debug_assertions, allow(clippy::panic_in_result_fn))]
fn check_slice_length<T, U: ArraySize>(slice: &[T]) -> Result<(), TryFromSliceError> {
debug_assert_eq!(Array::<(), U>::default().len(), U::USIZE);

if slice.len() != U::USIZE {
// Hack: `TryFromSliceError` lacks a public constructor
<&[T; 1]>::try_from([].as_slice())?;

#[cfg(debug_assertions)]
unreachable!();
}

Ok(())
Expand Down Expand Up @@ -400,7 +406,17 @@ impl<T, const N: usize> ArrayExt<T> for [T; N] {

/// Trait which associates a [`usize`] size and `ArrayType` with a
/// `typenum`-provided [`Unsigned`] integer.
pub trait ArraySize: Unsigned {
///
/// # Safety
///
/// `ArrayType` MUST be an array with a number of elements exactly equal to
/// [`Unsigned::USIZE`].
///
/// Failure to so will cause undefined behavior.
///
/// NOTE: do not implement this trait yourself. It is implemented for types in
/// [`typenum`].
pub unsafe trait ArraySize: Unsigned {
/// Array type which corresponds to this size.
type ArrayType<T>: AsRef<[T]> + AsMut<[T]> + IntoArray<T> + ArrayExt<T>;
}
Expand Down Expand Up @@ -457,7 +473,7 @@ macro_rules! impl_array_size {
}
}

impl ArraySize for typenum::$ty {
unsafe impl ArraySize for typenum::$ty {
type ArrayType<T> = [T; $len];
}

Expand Down

0 comments on commit beb2b92

Please sign in to comment.