Struct with flexible array member

I'm trying to create an EthernetFrame struct. In C I could use the flexible array member as the last field in the struct. How do I do this in Rust? Also how do I create an instance of this struct from the Vec<u8> buffer I have? (Since pointer casting only works on primitive types in rust.)

The total size of the Payload can vary between 50 bytes to 1504 bytes.

#[repr(C, packed)]
pub struct EthernetFrame {
    dst: [u8;6],
    src: [u8;6],
    ether_type: [u8;2],
    payload: <dynamically sized u8 array>,
}

What is the maximum size of your payload, and where is the length stored?

Sorry, forgot to add the length of the payload to the question. The total size of the Payload can vary between 50 bytes to 1504 bytes.

You would have to use a 1504 byte array then.

1 Like

Right thanks!. How about creating a new instance of the struct from the Vec<u8> buffer?(The capacity of the vector matches with the size of the EthernetFrame struct)

Consider using this design instead?

pub struct EthernetFrame {
    data: Vec<u8>,
}

impl EthernetFrame {
    pub fn dst(&self) -> [u8; 6] {
        let mut dst = [0; 6];
        dst.copy_from_slice(&self.data[0..6]);
        dst
    }
    pub fn src(&self) -> [u8; 6] {
        let mut src = [0; 6];
        src.copy_from_slice(&self.data[6..12]);
        src
    }
    pub fn ether_type(&self) -> [u8; 2] {
        let mut ether_type = [0; 2];
        ether_type.copy_from_slice(&self.data[12..14]);
        ether_type
    }
    pub fn payload(&self) -> &[u8] {
        &self.data[14..]
    }
}
2 Likes

Or this, even.

pub struct EthernetFrame {
    data: Box<[u8]>,
}

impl EthernetFrame {
    pub fn new(data: &[u8]) -> Self {
        Self {
            data: data.to_vec().into_boxed_slice()
        }
    }
}

Then the struct doesn't have both a length and capacity field.

1 Like

Thank you so much!

There are two more ways you can do it in Rust:

  • Actually flexible member with "coerce unsized" (https://doc.rust-lang.org/nomicon/coercions.html). Sorry, I can't find a clear example for this, but you can have a struct that ends with a slice and uses a fat pointer to track size of that slice.

  • A less adventurous arrayvec that preallocates max space, and tracks how much is used. It behaves like Vec, but doesn't cause heap allocation. It does have its own representation, so you can't cast raw packed data to it, so that's not for C-style lazy parsing.

1 Like