Call common method on enum different types

Hello sorry, for the title, I don't know how to clearly explain the problem in few words. Anyway, here it is: I have an enum which can have 2 different types:

enum DataSize {
	Word(i16),
	DWord(i32),
}

Then I have an expression that set a variable with one of this type, base on something else:

let value = match size {
	2 => DataSize::DWord(value as i32),
	_ => DataSize::Word(value as i16)
};

And later, I need to convert the value into an array of bytes:

let bytes = value.to_be_bytes()

The method to_be_bytes is implemented for both the type i16 and i32, but, of course, not for my type DataSize and so my code does not compile. How can I workaround such a problem?

If the method returned the same type for all variants, you could simply implement in on the enum by dispatching to the underlying implementations. In this case this can't be done directly though, since i16::to_be_bytes returns [u8; 2], and i32::to_be_bytes returns [u8; 4]. What type do you want bytes to have?

1 Like

Thank you for your answer. The variable bytes can be of different types, [u8; 4] if size is 2, [u8; 2] otherwise

Variables must always be only one type, so you need to choose. This is essentially the reason you need an enum in the first place.

ArrayVec can work for this, or you can take a &mut [u8; 4] as an argument and return &mut [u8] that's either 2 or 4 elements long.

fn to_be_bytes(&self) -> arrayvec::ArrayVec<u8, 4> {
    match self {
        Self::Word(w) => w.to_be_bytes().into(),
        Self::DWord(dw) => dw.to_be_bytes().into(),
    }
}

fn to_be_bytes<'a>(&self, buf: &'a mut [u8; 4]) -> &'a mut [u8] {
    match self {
        Self::Word(w) => {
            let buf = &mut buf[..2];
            buf.copy_from_slice(&w.to_be_bytes());
            buf
        }
        Self::DWord(dw) => {
            let buf = &mut buf[..4];
            buf.copy_from_slice(&dw.to_be_bytes());
            buf
        }
    }
}
2 Likes

Thank you all. In the end I go for a simple solution. The variable bytes should be passed to a function that receive a [u8; x], so I declare value as i32, so bytes is of type [u8; 4] and then pass a slice to the function depending on the variable size:

let index = match size {
    2 => 0,
    _ => 2,
}

send_bytes(bytes[index..]);

Another possibility for solving the original problem would be to flip around the representation, and store the bytes instead of the integer. This way it's possible to return a slice.

enum DataSize {
	Word([u8; 2]),
	DWord([u8; 4]),
}
impl DataSize {
    pub fn bytes(&self) -> &[u8] {
        match self {
            DataSize::Word(w) => &w[..],
            DataSize::DWord(dw) => &w[..],
        }
    }
    pub fn value() -> i32 {
        match self {
            DataSize::Word(w) => i32::from(i16::from_be_bytes(w)),
            DataSize::DWord(dw) => i32::from_be_bytes(dw),
        }
    }
}
2 Likes