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}