3 releases
new 0.1.2 | May 1, 2025 |
---|---|
0.1.1 | May 1, 2025 |
0.1.0 | May 1, 2025 |
#1272 in Rust patterns
21KB
66 lines
Subslice to Array
Provides functions and traits for easily extracting a subslice from a slice and converting the subslice to an array, array reference, or mutable array reference, with compile-time checks on the subslice's length.
Exclusively uses const generics instead of macros.
The three traits SubsliceToArray
, SubsliceToArrayRef
, and SubsliceToArrayMut
are the main way this crate should be used; three corresponding functions are provided in case
a full turbofish is needed for specifying the slice's type or the desired array length.
Motivation
It is already possible to convert a subslice into an array with runtime validity checks (some of which will likely be optimized away, if the index range is constant):
let data_slice: &[u32] = &[1, 2, 3];
let first_two: [u32; 2] = data_slice[0..2].try_into().unwrap();
However, the following code still compiles, and would panic at runtime:
let data_slice: &[u32] = &[1, 2, 3];
let first_two: [u32; 2] = data_slice[1..2].try_into().unwrap();
For the sake of catching bugs in advance, it would be preferable to perform checks at compile time for cases similar to above, where the range used to create a subslice (from some source slice) is statically known. Additionally, given that using macros can impact compile times, and given how simply these checks can be performed with const generics, it would be preferable to use const generics in stable versions of Rust which support the necessary features.
This crate successfully shifts some of these checks to compile time with const generics;
it confirms that the range is valid -- i.e., the start of the range is less than or equal to the
end of the range -- and that the length of the range is equal to the length of the desired array
length. This is sufficient to imply that .try_into().unwrap()
would not panic. However, we do not
and cannot check at compile time whether the slice is too short; the conversion functions here will
panic (at runtime) if indices in a range are out-of-bounds for a slice.
Examples
use subslice_to_array::{SubsliceToArray as _, SubsliceToArrayMut as _, SubsliceToArrayRef as _};
let data: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
assert_eq!(
data.subslice_to_array::<0, 4>(),
[0, 1, 2, 3],
);
assert_eq!(
data.subslice_to_array::<4, 9>(),
[4, 5, 6, 7, 8],
);
let data: &mut [u8] = &mut [0, 1, 2, 3, 4];
*data.subslice_to_array_mut::<1, 3>() = 0xffff_u16.to_le_bytes();
assert_eq!(
data,
&mut [0, 255, 255, 3, 4],
);
fn fn_that_only_gets_a_slice(bytes: &[u8]) -> Option<&[u8; 4]> {
if bytes.len() < 5 {
None
} else {
Some(bytes.subslice_to_array_ref::<1, 5>())
}
}
assert_eq!(
fn_that_only_gets_a_slice(data),
Some(&[255, 255, 3, 4]),
);
Similar crates
The arrayref and index-fixed crates perform essentially the same task as this crate, but with
macros. Note that arrayref has you specify only what would be START
and N
(and T
) here,
and thus does not need compile-time checks for whether START
and END
are together correct.
If you want to extract a subarray out of a source array instead of a source slice, there are also the sub-array and const-sub-array crates (though you can always convert the source array to a slice); the benefit of const-sub-array is that bounds checks are performed at compile time. (These two crates use macros as well.)
Lastly, the standard library (in core
, moreover) provides the option of
slice[START..END].try_into().unwrap()
.
Minimum supported Rust Version (MSRV)
As far as I can tell, this crate's functionality needs at least Rust 1.79 in order for the const generics to properly work. In particular, inline const was stabilized in 1.79, and I could not find a sufficient workaround for earlier versions of Rust.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.