Skip to main content

hybrid_array/
traits.rs

1//! Trait definitions.
2
3use crate::Array;
4use core::{
5    borrow::{Borrow, BorrowMut},
6    fmt::Debug,
7    ops::{Index, IndexMut, Range},
8};
9use typenum::Unsigned;
10
11/// Trait which associates a [`usize`] size and `ArrayType` with a
12/// `typenum`-provided [`Unsigned`] integer.
13///
14/// # Safety
15///
16/// `ArrayType` MUST be an array with a number of elements exactly equal to
17/// [`Unsigned::USIZE`]. Breaking this requirement will cause undefined behavior.
18///
19/// NOTE: This trait is effectively sealed and can not be implemented by third-party crates.
20/// It is implemented only for a number of types defined in [`typenum::consts`].
21#[diagnostic::on_unimplemented(note = "size may not be supported (see RustCrypto/hybrid-array#66)")]
22pub unsafe trait ArraySize: Unsigned + Debug {
23    /// Array type which corresponds to this size.
24    ///
25    /// This is always defined to be `[T; N]` where `N` is the same as
26    /// [`ArraySize::USIZE`][`typenum::Unsigned::USIZE`].
27    type ArrayType<T>: AssocArraySize<Size = Self>
28        + AsRef<[T]>
29        + AsMut<[T]>
30        + Borrow<[T]>
31        + BorrowMut<[T]>
32        + From<Array<T, Self>>
33        + Index<usize>
34        + Index<Range<usize>>
35        + IndexMut<usize>
36        + IndexMut<Range<usize>>
37        + Into<Array<T, Self>>
38        + IntoIterator<Item = T>;
39}
40
41/// Associates an [`ArraySize`] with a given type. Can be used to accept `[T; N]` const generic
42/// arguments and convert to [`Array`] internally.
43///
44/// This trait is also the magic glue that makes the [`ArrayN`][`crate::ArrayN`] type alias work.
45///
46/// # Example
47///
48/// ```
49/// use hybrid_array::{ArrayN, AssocArraySize};
50///
51/// pub fn example<const N: usize>(bytes: &[u8; N])
52/// where
53///     [u8; N]: AssocArraySize + AsRef<ArrayN<u8, N>>
54/// {
55///     // _arrayn is ArrayN<u8, N>
56///     let _arrayn = bytes.as_ref();
57/// }
58/// ```
59pub trait AssocArraySize: Sized {
60    /// Size of an array type, expressed as a [`typenum`]-based [`ArraySize`].
61    type Size: ArraySize;
62}
63
64impl<T, U> AssocArraySize for Array<T, U>
65where
66    U: ArraySize,
67{
68    type Size = U;
69}
70
71/// Obtain an `&Array` reference for a given type.
72///
73/// This provides functionality equivalent to `AsRef<Array>` or `Borrow<Array>`, but is deliberately
74/// implemented as its own trait both so it can leverage [`AssocArraySize`] to determine the
75/// array size, and also to avoid inference problems that occur when third party impls of traits
76/// like [`AsRef`] and [`Borrow`] are added to `[T; N]`.
77///
78/// # Usage with `[T; N]`
79///
80/// ```
81/// use hybrid_array::{Array, ArraySize, AsArrayRef};
82///
83/// pub fn getn_hybrid<T, U: ArraySize>(arr: &Array<T, U>, n: usize) -> &T {
84///     &arr[2]
85/// }
86///
87/// pub fn getn_generic<T, const N: usize>(arr: &[T; N], n: usize) -> &T
88/// where
89///     [T; N]: AsArrayRef<T>
90/// {
91///     getn_hybrid(arr.as_array_ref(), n)
92/// }
93///
94/// let array = [0u8, 1, 2, 3];
95/// let x = getn_generic(&array, 2);
96/// assert_eq!(x, &2);
97/// ```
98pub trait AsArrayRef<T>: AssocArraySize {
99    /// Converts this type into an immutable [`Array`] reference.
100    fn as_array_ref(&self) -> &Array<T, Self::Size>;
101}
102
103/// Obtain a `&mut Array` reference for a given type.
104///
105/// Companion trait to [`AsArrayRef`] for mutable references, equivalent to [`AsMut`] or
106/// [`BorrowMut`].
107pub trait AsArrayMut<T>: AsArrayRef<T> {
108    /// Converts this type into a mutable [`Array`] reference.
109    fn as_array_mut(&mut self) -> &mut Array<T, Self::Size>;
110}
111
112impl<T, U> AsArrayRef<T> for Array<T, U>
113where
114    U: ArraySize,
115{
116    fn as_array_ref(&self) -> &Self {
117        self
118    }
119}
120
121impl<T, U> AsArrayMut<T> for Array<T, U>
122where
123    U: ArraySize,
124{
125    fn as_array_mut(&mut self) -> &mut Self {
126        self
127    }
128}
129
130impl<T, U, const N: usize> AsArrayRef<T> for [T; N]
131where
132    Self: AssocArraySize<Size = U>,
133    U: ArraySize<ArrayType<T> = Self>,
134{
135    fn as_array_ref(&self) -> &Array<T, U> {
136        self.into()
137    }
138}
139
140impl<T, U, const N: usize> AsArrayMut<T> for [T; N]
141where
142    Self: AssocArraySize<Size = U>,
143    U: ArraySize<ArrayType<T> = Self>,
144{
145    fn as_array_mut(&mut self) -> &mut Array<T, U> {
146        self.into()
147    }
148}
149
150/// Extension trait for `[T]` providing methods for working with [`Array`].
151pub trait SliceExt<T>: sealed::Sealed {
152    /// Get a reference to an array from a slice, if the slice is exactly the size of the array.
153    ///
154    /// Returns `None` if the slice's length is not exactly equal to the array size.
155    fn as_hybrid_array<U: ArraySize>(&self) -> Option<&Array<T, U>>;
156
157    /// Get a mutable reference to an array from a slice, if the slice is exactly the size of the
158    /// array.
159    ///
160    /// Returns `None` if the slice's length is not exactly equal to the array size.
161    fn as_mut_hybrid_array<U: ArraySize>(&mut self) -> Option<&mut Array<T, U>>;
162
163    /// Splits the shared slice into a slice of `U`-element arrays, starting at the beginning
164    /// of the slice, and a remainder slice with length strictly less than `U`.
165    ///
166    /// # Panics
167    /// If `U` is 0.
168    fn as_hybrid_chunks<U: ArraySize>(&self) -> (&[Array<T, U>], &[T]);
169
170    /// Splits the exclusive slice into a slice of `U`-element arrays, starting at the beginning
171    /// of the slice, and a remainder slice with length strictly less than `U`.
172    ///
173    /// # Panics
174    /// If `U` is 0.
175    fn as_hybrid_chunks_mut<U: ArraySize>(&mut self) -> (&mut [Array<T, U>], &mut [T]);
176}
177
178impl<T> SliceExt<T> for [T] {
179    fn as_hybrid_array<U: ArraySize>(&self) -> Option<&Array<T, U>> {
180        Array::slice_as_array(self)
181    }
182
183    fn as_mut_hybrid_array<U: ArraySize>(&mut self) -> Option<&mut Array<T, U>> {
184        Array::slice_as_mut_array(self)
185    }
186
187    fn as_hybrid_chunks<U: ArraySize>(&self) -> (&[Array<T, U>], &[T]) {
188        Array::slice_as_chunks(self)
189    }
190
191    fn as_hybrid_chunks_mut<U: ArraySize>(&mut self) -> (&mut [Array<T, U>], &mut [T]) {
192        Array::slice_as_chunks_mut(self)
193    }
194}
195
196impl<T> sealed::Sealed for [T] {}
197
198mod sealed {
199    pub trait Sealed {}
200}
201
202#[cfg(test)]
203mod tests {
204    use super::{AsArrayMut, AsArrayRef, SliceExt};
205    use crate::{
206        Array,
207        sizes::{U2, U3},
208    };
209
210    type A = Array<u8, U2>;
211
212    #[test]
213    fn core_as_array_ref() {
214        assert_eq!([1, 2, 3].as_array_ref(), &Array([1, 2, 3]));
215    }
216
217    #[test]
218    fn core_as_array_mut() {
219        assert_eq!([1, 2, 3].as_array_mut(), &Array([1, 2, 3]));
220    }
221
222    #[test]
223    fn hybrid_as_array_ref() {
224        assert_eq!(A::from([1, 2]).as_array_ref(), &Array([1, 2]));
225    }
226
227    #[test]
228    fn hybrid_as_array_mut() {
229        assert_eq!(A::from([1, 2]).as_array_mut(), &Array([1, 2]));
230    }
231
232    #[test]
233    fn slice_as_hybrid_array() {
234        assert_eq!([1, 2].as_hybrid_array::<U3>(), None);
235        assert_eq!([1, 2, 3].as_hybrid_array::<U3>(), Some(&Array([1, 2, 3])));
236        assert_eq!([1, 2, 3, 4].as_hybrid_array::<U3>(), None);
237    }
238
239    #[test]
240    fn slice_as_mut_hybrid_array() {
241        assert_eq!([1, 2].as_mut_hybrid_array::<U3>(), None);
242        assert_eq!(
243            [1, 2, 3].as_mut_hybrid_array::<U3>(),
244            Some(&mut Array([1, 2, 3]))
245        );
246        assert_eq!([1, 2, 3, 4].as_mut_hybrid_array::<U3>(), None);
247    }
248
249    #[test]
250    fn slice_as_hybrid_chunks() {
251        let (slice_empty, rem_empty): (&[A], &[u8]) = [].as_hybrid_chunks::<U2>();
252        assert!(slice_empty.is_empty());
253        assert!(rem_empty.is_empty());
254
255        let (slice_one, rem_one) = [1].as_hybrid_chunks::<U2>();
256        assert!(slice_one.is_empty());
257        assert_eq!(rem_one, &[1]);
258
259        let (slice_aligned, rem_aligned) = [1u8, 2].as_hybrid_chunks::<U2>();
260        assert_eq!(slice_aligned, &[Array([1u8, 2])]);
261        assert_eq!(rem_aligned, &[]);
262
263        let (slice_unaligned, rem_unaligned) = [1u8, 2, 3].as_hybrid_chunks::<U2>();
264        assert_eq!(slice_unaligned, &[Array([1u8, 2])]);
265        assert_eq!(rem_unaligned, &[3]);
266    }
267
268    #[test]
269    fn slice_as_hybrid_chunks_mut() {
270        let (slice_empty, rem_empty): (&mut [A], &mut [u8]) = [].as_hybrid_chunks_mut::<U2>();
271        assert!(slice_empty.is_empty());
272        assert!(rem_empty.is_empty());
273
274        let mut arr1 = [1];
275        let (slice_one, rem_one) = arr1.as_hybrid_chunks_mut::<U2>();
276        assert!(slice_one.is_empty());
277        assert_eq!(rem_one, &[1]);
278
279        let mut arr2 = [1u8, 2];
280        let (slice_aligned, rem_aligned) = arr2.as_hybrid_chunks_mut::<U2>();
281        assert_eq!(slice_aligned, &mut [Array([1u8, 2])]);
282        assert_eq!(rem_aligned, &mut []);
283
284        let mut arr3 = [1u8, 2, 3];
285        let (slice_unaligned, rem_unaligned) = arr3.as_hybrid_chunks_mut::<U2>();
286        assert_eq!(slice_unaligned, &mut [Array([1u8, 2])]);
287        assert_eq!(rem_unaligned, &mut [3]);
288    }
289}