I have a trait that specifies a deserialization function for its implementor. This trait should take in a fixed size array of u8
where the length of this array depends on the implementer (e.g. type A
might be deserializable from 10 bytes, type B
might be deserializable from 50 bytes). Unfortunately I just can't get this trait to work when I want it to support the deserialization of arrays of a type implementing my trait, I always run into errors related to generics not being usable in const expressions. One important thing to note is that my code is running in a quite particular environment where I need to know the size of my calldata at compile time, so I can't switch this out for Vec
and be done with it unfortunately. Here's what I've tried so far:
Attempt 1:
trait MyTrait {
const ARR_LEN: usize;
fn deserialize<const N: usize>(array: [u8; N]) -> Self;
}
impl<const M: usize, T> MyTrait for [T; M]
where
T: MyTrait,
{
const ARR_LEN: usize = M * T::ARR_LEN;
fn deserialize<const N: usize>(array: [u8; N]) -> Self {
array
.chunks_exact(T::ARR_LEN)
.map(|chunk| T::deserialize::<{T::ARR_LEN}>(chunk.try_into().unwrap()))
.collect::<Vec<_>>()
.try_into()
.unwrap()
}
}
This gives an error in the T::deserialize::<{T::ARR_LEN}>
part of the deserialization function, namely that we can't use a type generic in a const expression.
Attempt 2:
trait MyTrait {
const ARR_LEN: usize;
fn deserialize(array: [u8; Self::ARR_LEN]) -> Self;
}
impl<const M: usize, T> MyTrait for [T; M]
where
T: MyTrait,
{
const ARR_LEN: usize = M * T::ARR_LEN;
fn deserialize(array: [u8; ARR_LEN]) -> Self {
array
.chunks_exact(T::ARR_LEN)
.map(|chunk| T::deserialize(chunk.try_into().unwrap()))
.collect::<Vec<_>>()
.try_into()
.unwrap()
}
}
This gives an error in the fn deserialize(array: [u8; ARR_LEN]) -> Self {
part, also that I can't use an generic in a const expression (since ARR_LEN
is constructed using T::ARR_LEN
).
Attempt 3:
trait MyTrait {
const ARR_LEN: usize;
type ArrayType: Sized;
fn deserialize(array: Self::ArrayType) -> Self;
}
impl<const M: usize, T> MyTrait for [T; M]
where
T: MyTrait,
{
const ARR_LEN: usize = M * T::ARR_LEN;
type ArrayType = [u8; Self::ARR_LEN];
fn deserialize(array: Self::ArrayType) -> Self {
array
.chunks_exact(T::ARR_LEN)
.map(|chunk| T::deserialize(chunk.try_into().unwrap()))
.collect::<Vec<_>>()
.try_into()
.unwrap()
}
}
This gives an error in the type ArrayType = [u8; Self::ARR_LEN]
part, as above that I can't use an generic in a const expression (since ARR_LEN
is constructed using T::ARR_LEN
here too).
Does anyone have an idea or different approach of how I could get this to work (without having to resort to using nightly with #![feature(generic_const_exprs)]
)?
You can use generic const parameter instead of associate const,
trait MyTrait<const N: usize> {
fn deser(array: [u8; N]) -> Self;
}
#[derive(Debug)]
struct MyStruct {
data: u8,
}
impl MyTrait<1> for MyStruct {
fn deser(array: [u8; 1]) -> Self {
Self { data: array[0] }
}
}
impl<const N: usize> MyTrait<N> for [MyStruct; N] {
fn deser(array: [u8; N]) -> Self {
array.map(|e| MyStruct::deser([e]))
}
}
fn main() {
let array = [0x01];
let my_struct = MyStruct::deser(array);
println!("{:?}", my_struct);
let array = [0x01, 0x02, 0x03];
let my_array = <[MyStruct; 3]>::deser(array);
println!("{:?}", my_array);
}
MyStruct { data: 1 }
[MyStruct { data: 1 }, MyStruct { data: 2 }, MyStruct { data: 3 }]
let array = [0x01, 0x02, 0x03];
let my_array = <[MyStruct; 4]>::deser(array);
error[E0308]: mismatched types
--> ***\src/main.rs:33:43
|
33 | let my_array = <[MyStruct; 4]>::deser(array);
| ---------------------- ^^^^^ expected an array with a fixed size of 4 elements, found one with 3 elements
| |
| arguments to this function are incorrect
|
note: associated function defined here
--> ***\src/main.rs:2:8
|
2 | fn deser(array: [u8; N]) -> Self;
| ^^^^^
For more information about this error, try `rustc --explain E0308`.