Skip to content

Commit

Permalink
hybrid-array: reference conversion support (#904)
Browse files Browse the repository at this point in the history
Adds a set of reference conversions to `Array<T, U>` as well as the
`ArrayOps` trait:

- `&Array<T, U>`: `From<&[T, N]>` + TryFrom<&[T]>
- `&mut Array<T, U>`: `From<&mut [T, N]>` + `TryFrom<&mut [T]>`

The implementation uses an unsafe pointer cast to convert a slice into
the corresponding reference newtype.

Additionally it adds the following panicking inherent methods to
`Array<T, U>` to ease migrating code based on `GenericArray`:

- `Array::ref_from_slice`: alternative to `GenericArray::from_slice`
- `Array::ref_from_mut_slice`: alternative to
  `GenericArray::from_mut_slice`
- `Array::clone_from_slice`: alternative to
  `GenericArray::clone_from_slice`

These methods are marked with TODOs to deprecate them before a final
v0.2.0 release, however for now they're not deprecate to ease a
migration.
  • Loading branch information
tarcieri committed May 30, 2023
1 parent 26232f7 commit b6cedcf
Showing 1 changed file with 119 additions and 1 deletion.
120 changes: 119 additions & 1 deletion hybrid-array/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
)]
#![forbid(unsafe_code)]
#![warn(
clippy::cast_lossless,
clippy::cast_possible_truncation,
Expand Down Expand Up @@ -67,6 +66,12 @@ pub trait ArrayOps<T, const N: usize>:
/// Create array from Rust's core array type.
fn from_core_array(arr: [T; N]) -> Self;

/// Create array reference from reference to Rust's core array type.
fn from_core_array_ref(arr: &[T; N]) -> &Self;

/// Create mutable array reference from reference to Rust's core array type.
fn from_core_array_mut(arr: &mut [T; N]) -> &mut Self;

/// Create array where each array element `T` is returned by the `cb` call.
fn from_fn<F>(mut cb: F) -> Self
where
Expand Down Expand Up @@ -140,6 +145,16 @@ macro_rules! impl_array_size {
Self(arr)
}

fn from_core_array_ref(array_ref: &[T; $len]) -> &Self {
// SAFETY: `$ty` is a `repr(transparent)` newtype for `[T; $len]`
unsafe { &*(array_ref.as_ptr() as *const Self) }
}

fn from_core_array_mut(array_ref: &mut [T; $len]) -> &mut Self {
// SAFETY: `$ty` is a `repr(transparent)` newtype for `[T; $len]`
unsafe { &mut *(array_ref.as_mut_ptr() as *mut Self) }
}

#[inline]
fn from_slice(slice: &[T]) -> Result<Self, TryFromSliceError>
where
Expand Down Expand Up @@ -307,6 +322,45 @@ where
pub fn as_mut_slice(&mut self) -> &mut [T] {
self.0.as_mut()
}

/// Convert the given slice into a reference to a hybrid array.
///
/// # Panics
///
/// Panics if the slice's length doesn't match the array type.
// TODO(tarcieri): deprecate this before the v0.2 release
// #[deprecated(since = "0.2.0", note = "use TryFrom instead")]
#[inline]
pub fn ref_from_slice(slice: &[T]) -> &Self {
slice.try_into().expect("slice length mismatch")
}

/// Convert the given mutable slice to a mutable reference to a hybrid array.
///
/// # Panics
///
/// Panics if the slice's length doesn't match the array type.
// TODO(tarcieri): deprecate this before the v0.2 release
// #[deprecated(since = "0.2.0", note = "use TryFrom instead")]
#[inline]
pub fn ref_from_mut_slice(slice: &mut [T]) -> &mut Self {
slice.try_into().expect("slice length mismatch")
}

/// Clone the contents of the slice as a new hybrid array.
///
/// # Panics
///
/// Panics if the slice's length doesn't match the array type.
// TODO(tarcieri): deprecate this before the v0.2 release
// #[deprecated(since = "0.2.0", note = "use TryFrom instead")]
#[inline]
pub fn clone_from_slice(slice: &[T]) -> Self
where
Self: Clone,
{
Self::ref_from_slice(slice).clone()
}
}

impl<T, U, const N: usize> AsRef<[T; N]> for Array<T, U>
Expand Down Expand Up @@ -364,6 +418,28 @@ where
}
}

impl<'a, T, U, const N: usize> From<&'a [T; N]> for &'a Array<T, U>
where
Array<T, U>: ArrayOps<T, N>,
U: ArraySize,
{
#[inline]
fn from(array_ref: &'a [T; N]) -> &'a Array<T, U> {
<Array<T, U>>::from_core_array_ref(array_ref)
}
}

impl<'a, T, U, const N: usize> From<&'a mut [T; N]> for &'a mut Array<T, U>
where
Array<T, U>: ArrayOps<T, N>,
U: ArraySize,
{
#[inline]
fn from(array_ref: &'a mut [T; N]) -> &'a mut Array<T, U> {
<Array<T, U>>::from_core_array_mut(array_ref)
}
}

impl<T, I, U> Index<I> for Array<T, U>
where
[T]: Index<I>,
Expand Down Expand Up @@ -402,5 +478,47 @@ where
}
}

impl<'a, T, U> TryFrom<&'a [T]> for &'a Array<T, U>
where
U: ArraySize,
{
type Error = TryFromSliceError;

#[inline]
fn try_from(slice: &'a [T]) -> Result<Self, TryFromSliceError> {
check_slice_length::<T, U>(slice)?;

// SAFETY: `Array<T, U>` is a `repr(transparent)` newtype for a core
// array with length checked above.
Ok(unsafe { *(slice.as_ptr() as *const Self) })
}
}

impl<'a, T, U> TryFrom<&'a mut [T]> for &'a mut Array<T, U>
where
U: ArraySize,
{
type Error = TryFromSliceError;

#[inline]
fn try_from(slice: &'a mut [T]) -> Result<Self, TryFromSliceError> {
check_slice_length::<T, U>(slice)?;

// SAFETY: `Array<T, U>` is a `repr(transparent)` newtype for a core
// array with length checked above.
Ok(unsafe { *(slice.as_ptr() as *mut Self) })
}
}

/// Byte array type.
pub type ByteArray<U> = Array<u8, U>;

/// Generate a [`TryFromSliceError`] if the slice doesn't match the given length.
fn check_slice_length<T, U: Unsigned>(slice: &[T]) -> Result<(), TryFromSliceError> {
if slice.len() != U::USIZE {
// Hack: `TryFromSliceError` lacks a public constructor
<&[T; 1]>::try_from([].as_slice())?;
}

Ok(())
}

0 comments on commit b6cedcf

Please sign in to comment.