Home > Blockchain >  Rust generics and compile time size
Rust generics and compile time size

Time:01-21

I have a function similar to the one below:

fn write( data: u8, buf: &mut dyn Write )
{
    let bytes = data.to_ne_bytes();
    buf.write(&bytes);
}

I would like to have this function for all the fixed sized types u8, i8, u16, i16 etc., similar to overloading in C . Is there a way to do that with generics in Rust?

I have tried the following, however, it does not compile:

use fixed::traits::Fixed;

fn write<T: Fixed<Bytes = [u8]>   Sized>( data: T, buf: &mut dyn Write )
{
    let bytes = data.to_ne_bytes();
    buf.write(&bytes);
}

I get the following error:

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time

    |
4   |     let bytes = data.to_ne_bytes();
    |         ^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `[u8]`
    = note: all local variables must have a statically known size
    = help: unsized locals are gated as an unstable feature

I also tried something like this:

fn write<T: Fixed<Bytes = [u8]>   Sized>( data: T, buf: &mut dyn Write )
{
    let bytes:[u8; mem::size_of_val(data)] = data.to_ne_bytes();
    buf.write(&bytes);
}

Unfortunately that doesnt work either. I get this compile error:

error[E0435]: attempt to use a non-constant value in a constant

    |
146 | fn write<T: Fixed<Bytes = [u8]>   Sized>( data: T, buf: &mut dyn Write )
    |                                            ---- this would need to be a `const`
...
149 |     let bytes:[u8; mem::size_of_val(data)] = data.to_ne_bytes();
    |                                     ^^^^


Edit:

The suggested duplicate is not usable in my case. The trait that is used there does not allow me to have a type where its size is known at compile time

CodePudding user response:

Here's an example using a custom trait:

use std::io::Write;

trait ToBytes {
    type Output;
    
    fn to_ne_bytes(self) -> Self::Output;
    
    // TODO: maybe add to_le_bytes and/or to_be_bytes?
}

impl ToBytes for u8 {
    type Output = [u8; 1];
    
    fn to_ne_bytes(self) -> Self::Output {
        [self]
    }
}
impl ToBytes for u16 {
    type Output = [u8; 2];
    
    fn to_ne_bytes(self) -> Self::Output {
        self.to_ne_bytes()
    }
}
impl ToBytes for u32 {
    type Output = [u8; 4];
    
    fn to_ne_bytes(self) -> Self::Output {
        self.to_ne_bytes()
    }
}
impl ToBytes for u64 {
    type Output = [u8; 8];
    
    fn to_ne_bytes(self) -> Self::Output {
        self.to_ne_bytes()
    }
}
// TODO: signed integers

fn write<T>(data: T, buf: &mut dyn Write)
where
    T: ToBytes,
    <T as ToBytes>::Output: AsRef<[u8]>,
{
    let bytes = data.to_ne_bytes();
    buf.write(bytes.as_ref());
}

fn main() {
    let x: u8 = 5;
    let mut buf: Vec<u8> = Vec::new();
    
    write(x, &mut buf);
}

playground

CodePudding user response:

Assuming the implementers of Fixed set Bytes to be fixed-size u8 arrays, you can use const generics and make write generic over the size of the u8 array in Bytes:

fn write<const N: usize, T: Fixed<Bytes = [u8; N]>>(data: T, buf: &mut dyn Write) {
    let bytes = data.to_ne_bytes();
    buf.write(&bytes);
}

Playground

  • Related