subslice_to_array/
lib.rs

1#![no_std]
2
3// See https://linebender.org/blog/doc-include for this README inclusion strategy
4//! [SubsliceToArray]: crate::SubsliceToArray
5//! [SubsliceToArrayRef]: crate::SubsliceToArrayRef
6//! [SubsliceToArrayMut]: crate::SubsliceToArrayMut
7// File links are not supported by rustdoc
8//! [LICENSE-APACHE]: https://github.com/robofinch/subslice-to-array/blob/main/LICENSE-APACHE
9//! [LICENSE-MIT]: https://github.com/robofinch/subslice-to-array/blob/main/LICENSE-MIT
10//!
11//! <style>
12//! .rustdoc-hidden { display: none; }
13//! </style>
14#![doc =  include_str!("../README.md")]
15
16
17/// Conversion from a subslice with copyable data to an array, with compile-time checks
18/// on the `START..END` range used to index into a source slice.
19pub trait SubsliceToArray<T, const N: usize> {
20    /// Compile-time checked version of code like `slice[START..END].try_into().unwrap()`
21    /// for converting part of a slice into an array of length `N`.
22    ///
23    /// This is only implemented for slices `&[T]` where `T` is `Copy`.
24    ///
25    /// The function confirms at compile time that `START <= END` and `N == END - START`.
26    /// A compile-time error is thrown if this requirement is not met.
27    ///
28    /// This internally uses [`subslice_to_array`]; if you need to explicitly set the `T` or `N`
29    /// generics, you should directly use [`subslice_to_array`], but in most cases this wrapper is
30    /// more convenient.
31    ///
32    /// # Panics
33    /// Panics if any index in the `START..END` range is out-of-bounds for the provided slice.
34    /// We cannot check that at compile time.
35    ///
36    /// # Examples
37    /// ```
38    /// use subslice_to_array::SubsliceToArray as _;
39    /// let data: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
40    /// assert_eq!(
41    ///     data.subslice_to_array::<0, 4>(),
42    ///     [0, 1, 2, 3],
43    /// );
44    /// assert_eq!(
45    ///     data.subslice_to_array::<4, 9>(),
46    ///     [4, 5, 6, 7, 8],
47    /// );
48    ///
49    /// fn fn_that_only_gets_a_slice(bytes: &[u8]) -> Option<[u8; 4]> {
50    ///     if bytes.len() < 5 {
51    ///         None
52    ///     } else {
53    ///         Some(bytes.subslice_to_array::<1, 5>())
54    ///     }
55    /// }
56    ///
57    /// assert_eq!(
58    ///     fn_that_only_gets_a_slice(data),
59    ///     Some([1, 2, 3, 4]),
60    /// );
61    ///
62    /// let data_vec: Vec<u8> = vec![4, 2];
63    /// let data_arr: [u8; 2] = data_vec.subslice_to_array::<0, 2>();
64    /// assert_eq!(data_arr, [4, 2]);
65    ///
66    /// // This is a pretty absurd edge case, but it works.
67    /// let unit_arr: [(); usize::MAX] = [(); usize::MAX];
68    /// let unit_slice: &[()] = unit_arr.as_slice();
69    /// let round_trip: [(); usize::MAX] = unit_slice.subslice_to_array::<0, {usize::MAX}>();
70    /// ```
71    ///
72    /// # Compile fail examples
73    /// If `END - START` were computed in release mode without checking that `START <= END`,
74    /// the below computation would wrap around to `2`. Since we do perform that check, this
75    /// fails to compile.
76    /// ```compile_fail
77    /// # // Should emit E0080, but we can't specify that on stable, only nightly.
78    /// use subslice_to_array::SubsliceToArray as _;
79    /// let data: &[u32] = &[0];
80    /// let data_2: [u32; 2] = data.subslice_to_array::<{usize::MAX}, 1>();
81    /// ```
82    ///
83    /// Below, `END - START` is not equal to `N`.
84    /// ```compile_fail
85    /// # // Should emit E0080, but we can't specify that on stable, only nightly.
86    /// use subslice_to_array::SubsliceToArray as _;
87    /// let data: &[u32] = &[0, 1, 2];
88    /// let data_3: [u32; 3] = data.subslice_to_array::<1, 3>();
89    /// ```
90    fn subslice_to_array<const START: usize, const END: usize>(&self) -> [T; N];
91}
92
93impl<T: Copy, const N: usize> SubsliceToArray<T, N> for [T] {
94    #[inline]
95    fn subslice_to_array<const START: usize, const END: usize>(&self) -> [T; N] {
96        subslice_to_array::<START, END, T, N>(self)
97    }
98}
99
100/// Conversion from a subslice to a reference to an array, with compile-time checks
101/// on the `START..END` range used to index into a source slice.
102pub trait SubsliceToArrayRef<T, const N: usize> {
103    /// Compile-time checked version of code like `slice[START..END].try_into().unwrap()`
104    /// for converting part of a slice into a reference to an array of length `N`.
105    ///
106    /// The function confirms at compile time that `START <= END` and `N == END - START`.
107    /// A compile-time error is thrown if this requirement is not met.
108    ///
109    /// This internally uses [`subslice_to_array_ref`]; if you need to explicitly set the
110    /// `T` or `N` generics, you should directly use [`subslice_to_array_ref`], but in most cases
111    /// this wrapper is more convenient.
112    ///
113    /// # Panics
114    /// Panics if any index in the `START..END` range is out-of-bounds for the provided slice.
115    /// We cannot check that at compile time.
116    ///
117    /// # Examples
118    /// ```
119    /// use subslice_to_array::SubsliceToArrayRef as _;
120    /// let data: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
121    /// assert_eq!(
122    ///     data.subslice_to_array_ref::<0, 4>(),
123    ///     &[0, 1, 2, 3],
124    /// );
125    /// assert_eq!(
126    ///     data.subslice_to_array_ref::<4, 9>(),
127    ///     &[4, 5, 6, 7, 8],
128    /// );
129    ///
130    /// fn fn_that_only_gets_a_slice(bytes: &[u8]) -> Option<&[u8; 4]> {
131    ///     if bytes.len() < 5 {
132    ///         None
133    ///     } else {
134    ///         Some(bytes.subslice_to_array_ref::<1, 5>())
135    ///     }
136    /// }
137    ///
138    /// assert_eq!(
139    ///     fn_that_only_gets_a_slice(data),
140    ///     Some(&[1, 2, 3, 4]),
141    /// );
142    ///
143    /// let data_vec: Vec<u8> = vec![4, 2];
144    /// let data_arr: &[u8; 2] = data_vec.subslice_to_array_ref::<0, 2>();
145    /// assert_eq!(data_arr, &[4, 2]);
146    ///
147    /// // This is a pretty absurd edge case, but it works.
148    /// let unit_arr: [(); usize::MAX] = [(); usize::MAX];
149    /// let unit_slice: &[()] = unit_arr.as_slice();
150    /// let unit_arr_ref: &[(); usize::MAX] = unit_slice.subslice_to_array_ref::<0, {usize::MAX}>();
151    /// ```
152    ///
153    /// # Compile fail examples
154    /// If `END - START` were computed in release mode without checking that `START <= END`,
155    /// the below computation would wrap around to `2`. Since we do perform that check, this
156    /// fails to compile.
157    /// ```compile_fail
158    /// # // Should emit E0080, but we can't specify that on stable, only nightly.
159    /// use subslice_to_array::SubsliceToArrayRef as _;
160    /// let data: &[u32] = &[0];
161    /// let data_2: &[u32; 2] = data.subslice_to_array_ref::<{usize::MAX}, 1>();
162    /// ```
163    ///
164    /// Below, `END - START` is not equal to `N`.
165    /// ```compile_fail
166    /// # // Should emit E0080, but we can't specify that on stable, only nightly.
167    /// use subslice_to_array::SubsliceToArrayRef as _;
168    /// let data: &[u32] = &[0, 1, 2];
169    /// let data_3: &[u32; 3] = data.subslice_to_array_ref::<1, 3>();
170    /// ```
171    fn subslice_to_array_ref<const START: usize, const END: usize>(&self) -> &[T; N];
172}
173
174impl<T, const N: usize> SubsliceToArrayRef<T, N> for [T] {
175    #[inline]
176    fn subslice_to_array_ref<const START: usize, const END: usize>(&self) -> &[T; N] {
177        subslice_to_array_ref::<START, END, T, N>(self)
178    }
179}
180
181/// Conversion from a subslice to a mutable reference to an array, with compile-time checks
182/// on the `START..END` range used to mutably index into a source slice.
183pub trait SubsliceToArrayMut<T, const N: usize> {
184    /// Compile-time checked version of code like `(&mut slice[START..END]).try_into().unwrap()`
185    /// for converting part of a mutable slice into a mutable reference to an array of length `N`.
186    ///
187    /// The function confirms at compile time that `START <= END` and `N == END - START`.
188    /// A compile-time error is thrown if this requirement is not met.
189    ///
190    /// This internally uses [`subslice_to_array_mut`]; if you need to explicitly set the
191    /// `T` or `N` generics, you should directly use [`subslice_to_array_mut`], but in most cases
192    /// this wrapper is more convenient.
193    ///
194    /// # Panics
195    /// Panics if any index in the `START..END` range is out-of-bounds for the provided slice.
196    /// We cannot check that at compile time.
197    ///
198    /// # Examples
199    /// ```
200    /// use subslice_to_array::SubsliceToArrayMut as _;
201    /// let data: &mut [u8] = &mut [0, 1, 2, 3, 4];
202    /// *data.subslice_to_array_mut::<1, 3>() = 0xffff_u16.to_le_bytes();
203    /// assert_eq!(
204    ///     data,
205    ///     &mut [0, 255, 255, 3, 4],
206    /// );
207    ///
208    /// let data: &mut [u8] = &mut [0, 1, 2, 3, 4, 5, 6, 7, 8];
209    /// assert_eq!(
210    ///     data.subslice_to_array_mut::<0, 4>(),
211    ///     &mut [0, 1, 2, 3],
212    /// );
213    /// assert_eq!(
214    ///     data.subslice_to_array_mut::<4, 9>(),
215    ///     &mut [4, 5, 6, 7, 8],
216    /// );
217    ///
218    /// fn fn_that_only_gets_a_slice(bytes: &mut [u8]) -> Option<&mut [u8; 4]> {
219    ///     if bytes.len() < 5 {
220    ///         None
221    ///     } else {
222    ///         Some(bytes.subslice_to_array_mut::<1, 5>())
223    ///     }
224    /// }
225    ///
226    /// assert_eq!(
227    ///     fn_that_only_gets_a_slice(data),
228    ///     Some(&mut [1, 2, 3, 4]),
229    /// );
230    ///
231    /// let mut data_vec: Vec<u8> = vec![4, 2];
232    /// let data_arr: &mut [u8; 2] = data_vec.subslice_to_array_mut::<0, 2>();
233    /// assert_eq!(data_arr, &mut [4, 2]);
234    ///
235    /// // This is a pretty absurd edge case, but it works.
236    /// let mut unit_arr: [(); usize::MAX] = [(); usize::MAX];
237    /// let unit_slice: &mut [()] = unit_arr.as_mut_slice();
238    /// let unit_arr_ref: &mut [(); usize::MAX] = unit_slice
239    ///     .subslice_to_array_mut::<0, {usize::MAX}>();
240    /// ```
241    ///
242    /// # Compile fail examples
243    /// If `END - START` were computed in release mode without checking that `START <= END`,
244    /// the below computation would wrap around to `2`. Since we do perform that check, this
245    /// fails to compile.
246    /// ```compile_fail
247    /// # // Should emit E0080, but we can't specify that on stable, only nightly.
248    /// use subslice_to_array::SubsliceToArrayMut as _;
249    /// let data: &[u32] = &[0];
250    /// let data_2: &[u32; 2] = data.subslice_to_array_mut::<{usize::MAX}, 1>();
251    /// ```
252    ///
253    /// Below, `END - START` is not equal to `N`.
254    /// ```compile_fail
255    /// # // Should emit E0080, but we can't specify that on stable, only nightly.
256    /// use subslice_to_array::SubsliceToArrayMut as _;
257    /// let data: &[u32] = &[0, 1, 2];
258    /// let data_3: &[u32; 3] = data.subslice_to_array_mut::<1, 3>();
259    /// ```
260    fn subslice_to_array_mut<const START: usize, const END: usize>(&mut self) -> &mut [T; N];
261}
262
263impl<T, const N: usize> SubsliceToArrayMut<T, N> for [T] {
264    #[inline]
265    fn subslice_to_array_mut<const START: usize, const END: usize>(&mut self) -> &mut [T; N] {
266        subslice_to_array_mut::<START, END, T, N>(self)
267    }
268}
269
270/// Version of [`SubsliceToArray::subslice_to_array`] with all generic parameters listed.
271/// You should probably use this function (and a turbofish) only if the compiler cannot infer
272/// `T` or `N`.
273///
274/// See [`SubsliceToArray::subslice_to_array`] for a more convenient function with identical
275/// behavior, and for documentation about this function's behavior.
276#[inline]
277pub fn subslice_to_array<const START: usize, const END: usize, T: Copy, const N: usize>(
278    slice: &[T],
279) -> [T; N] {
280    *subslice_to_array_ref::<START, END, T, N>(slice)
281}
282
283/// Version of [`SubsliceToArrayRef::subslice_to_array_ref`] with all generic parameters listed.
284/// You should probably use this function (and a turbofish) only if the compiler cannot infer
285/// `T` or `N`.
286///
287/// See [`SubsliceToArrayRef::subslice_to_array_ref`] for a more convenient function with identical
288/// behavior, and for documentation about this function's behavior.
289#[inline]
290pub fn subslice_to_array_ref<const START: usize, const END: usize, T, const N: usize>(
291    slice: &[T],
292) -> &[T; N] {
293    const {
294        assert!(
295            START.checked_add(N).is_some(),
296            "`START + N` would overflow a usize in subslice_to_array or subslice_to_array_ref",
297        );
298        assert!(
299            // Note that `N` is nonzero and `START + N` does not overflow,
300            // so this both ensures `START <= END` and `N == END - START`.
301            START + N == END,
302            "subslice_to_array or subslice_to_array_ref was called with incorrect START/END bounds",
303        );
304    }
305
306    // The slice has the same length as the target array, so `try_into` succeeds
307    slice[START..END].try_into().unwrap()
308}
309
310/// Version of [`SubsliceToArrayMut::subslice_to_array_mut`] with all generic parameters listed.
311/// You should probably use this function (and a turbofish) only if the compiler cannot infer
312/// `T` or `N`.
313///
314/// See [`SubsliceToArrayMut::subslice_to_array_mut`] for a more convenient function with identical
315/// behavior, and for documentation about this function's behavior.
316#[inline]
317pub fn subslice_to_array_mut<const START: usize, const END: usize, T, const N: usize>(
318    slice: &mut [T],
319) -> &mut [T; N] {
320    const {
321        assert!(
322            START.checked_add(N).is_some(),
323            "`START + N` would overflow a usize in subslice_to_array_mut",
324        );
325        assert!(
326            // Note that `N` is nonzero and `START + N` does not overflow,
327            // so this both ensures `START <= END` and `N == END - START`.
328            START + N == END,
329            "subslice_to_array_mut was called with incorrect START/END bounds",
330        );
331    }
332
333    // The slice has the same length as the target array, so `try_into` succeeds
334    (&mut slice[START..END]).try_into().unwrap()
335}