Const generic trait impls

Hey,
I'm new to the const generic game and got a question about my code:

pub trait ToBytes<const L: usize> {
    fn to_bytes(&self) -> [u8; L];
}

pub struct Writer<const L: usize, T: ToBytes<L>> {
    // stuff here
}

impl<const L: usize, T: ToBytes<L>> WALWriter<L, T> {
    // impls
}

pub struct FourBytes(pub u32);
impl ToBytes<4> for FourBytes {
    fn to_bytes(&self) -> [u8; 4] {
        self.0.to_le_bytes()
    }
}

Is there an semi elegant way to omit the const L:usize on my Writer as it may get the const size from T itself?

I'd like to write something like

let mut writer = Writer<FourBytes>::new();

but currently have to write

let mut writer = Writer<4, FourBytes>::new();

Is this the intended, ideomatic way or am I missing something huge?

Greetings from Hamburg

Yes, you are missing the fact that Rust doesn't have const generics. What it does have is called const generics MVP and that MVP moniker is not just there for show.

What you want to do is perfectly achievable on nightly, but as you can guess there are no guarantees that what's working today on nightly would continue to work tomorrow.

3 Likes

Would something like this work for you?

pub trait ToBytes {
    type Bytes;
    fn to_bytes(&self) -> Self::Bytes;
}

pub struct Writer<T: ToBytes> {
    // stuff here
    buffer: T::Bytes,
}

impl<const L: usize, T: ToBytes<Bytes=[u8;L]>> Writer<T> {
    // impls
}

pub struct FourBytes(pub u32);
impl ToBytes for FourBytes {
    type Bytes = [u8; 4];
    fn to_bytes(&self) -> [u8; 4] {
        self.0.to_le_bytes()
    }
}

The nightly-only solution @VorfeedCanal is referring to would look like this.

#![allow(incomplete_features)]
#![feature(generic_const_exprs)]

trait ToBytes {
    const LENGTH: usize;
    fn to_bytes(&self) -> [u8; Self::LENGTH];
}

pub struct FourBytes(pub u32);

impl ToBytes for FourBytes {
    const LENGTH: usize = std::mem::size_of::<u32>();

    fn to_bytes(&self) -> [u8; Self::LENGTH] {
        self.0.to_le_bytes()
    }
}

(playground)

It'll probably work fine for your use case, but keep in mind that I've had to ignore the incomplete_features lint for a reason - more complicated LENGTHs might trigger an ICE.

On the plus side, you'll almost certainly be able to use the same code when the feature is stabilised because that generic_const_exprs feature flag just enables extra behaviour in the compiler without touching syntax.