Returning a generic-sized array

Hi,

I'm struggling to think about how to return an array of type [T; C] for constrained value of C. If I have a struct

pub struct Pixel<const C: usize> {
    r: u8,
    g: u8,
    b: u8,
    a: u8,
}

where C can either be 3 or 4 (checked at runtime in the new function), how could I go about returning an array of size C, such as:

pub fn as_bytes(&self) -> [u8; C] {
    match C {
        3 => [self.r, self.g, self.b],
        4 => [self.r, self.g, self.b, self.a],
        _ => panic!("Unexpected channel number {}", C),
    }
}

the problem is that C is a compile-time constant however in your function as_bytes you are trying to return 2 different things based on some runtime logic. in case of 3, you are returning a [u8;3] on the other part [u8;4]. but C can be either 3 or 4 at runtime on a thing you can do is to pad your array to always have 4 elements

2 Likes

The number of bytes returned by a function must be known ahead of time to the caller of the function, as a compile-time constant. This is a hard limitation of the calling convention, so you can never make the size decision in the function body.

This should work:

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

impl ToBytes for Pixel<3> {}
impl ToBytes for Pixel<4> {}
4 Likes

You'll probably want/need different implementations for different (compile-time-determined) values of the const parameter. Here's a few examples of various degrees of abstraction. Using a trait will make it easier to be generic in places that are somewhat agnostic about the how many channels you have, but need access to the channels. E.g.

// If you've only implemented `AsBytes` for `Pixel<3>` and `Pixel<4>`,
// you won't be able to pass anything but those two into `f`
fn f<P: AsBytes>(pixels: Vec<P>) { /* ... */ }

You could optimize for writing/code-size by pulling some tricks using From to go from four channels to three or something, and leaning on optimization, but I don't know that it's worth it.

3 Likes

Thanks for all of your answers, they've really cleared it up for me, and I'll be using a trait abstraction as suggested.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.