Summary
I'd like to discuss the idea of adding new types that implement core::slice::SliceIndex
to be able to get arrays from slices.
I'm proposed an API where constant-time versions of core::ops::Range
(and other ranges) are introduced to libcore so the standard slice.get(range)
and slice[range]
operations can be used to obtain arrays.
Motivation
I've found a common code pattern used at my work when decoding data from raw bytes and it is the following:
let my_integer = u64::from_le_bytes(bytes[..CONST_EQ_TO_8].try_into().expect("u64s are 8 bytes long"));
I felt uneasy about having an expect/unwrap
that can easily break if someone changes CONST_EQ_TO_8
by accident. But at the same time I naively thought that the compiler wasn't smart enough to optimize the second bounds check so sometimes I even used stuff like
let my_integer = u64::from_le_bytes(unsafe { bytes[..CONST_EQ_TO_8] as *const [u8] as *const [u8; 8] });
After I discovered that I was dangerously introducing unsafe code under the excuse of performance and then discovering that the compiler is smart enough to do that without unsafe. I thought that maybe this should be in a library to prevent others from doing what I did.
How to abstract this?
My first idea was to just add a bunch of methods to [T]
like:
fn get_as_array<const N: usize>(&self) -> Option<&[T; N]> {
self.get(..N).map(|slice: &[T]| <&[T; N]>::try_from(slice).unwrap())
}
so I spoke with @oli_obk about it and he thought that this could be done better by introducing something like a constant time version of core::ops::RangeTo
and so on. So I wrote the following crate to experiment a bit with the syntax: GitHub - pvdrz/const-index. This crate compiles in stable as no nightly features were used to write it.
The goal would be to add types like this:
pub struct ConstRangeTo<const MAX: usize>;
and implement std::slice::SliceIndex
to them with type Output = &[T; MAX];
so you can just use the regular get
and index
methods:
let my_integer = u64::from_le_bytes(bytes[ConstRangeTo<CONST_EQ_TO_8>]);
I went a bit farther and wrote a macro to build this const ranges:
let my_integer = u64::from_le_bytes(bytes[cindex!(..CONST_EQ_TO_8)]);
maybe it is a bit ambitious but it would be really cool to introduce some built-in syntax for this into rust itself:
let my_integer = u64::from_le_bytes(bytes[const ..CONST_EQ_TO_8]);
If you have any feedback or want to play with it you can clone the repository and write your own code inside examples/playground.rs
. I haven't published the crate to cargo yet because I'd like to have some opinions before committing to a first version.
Thanks