How to get a Enum variant generically in Enum Vec (don't care about its value)?

Sry for my poor English and Rust..
First, I have a Network Layer Enum like this: (i just simplify it)

pub enum Layer {
    Ethernet(EthernetHeader),
    Ipv4(Ipv4Header),
    Ipv6(Ipv6Header),
    Udp(UdpHeader),
    Tcp(TcpHeader),
    Modbus(ModbusHeader),
    Error(Vec<[u8]>),
    ...
}

and the packet Vec store those Layers might like:

pub struct Packet {
    layers: Vec<Layer>
}

let packet = Packet{ layers: vec!(EthernetHeader::new(), Ipv4Header::new(), TcpHeader::new(), ModbusHeader::new()) };

So, there's a problem, if i want to get any specific layer enum in this vec, i need to impl get_xx_layer function for all enum variant like:

impl Packet {
    fn get_tcp_layer(&self) -> Option<&Layer>
    {
        for layer in self.layers.iter() {
            if let Layer::Tcp(_) = layer {
                return Some(layer);
            }
        }  
        None   
    }

    fn get_udp_layer(&self) -> Option<&Layer>
    {
        for layer in self.layers.iter() {
            if let Layer::Udp(_) = layer {
                return Some(layer);
            }
        }  
        None   
    }

    ...
}

that's very redundant. Is there any way to impl a func which get a enum variant as its param (or impl a LayerType to represent each layer) and output the eligible enum? just like:

fn get_layer(&self, LayerType) -> Option<&Layer> {
    ...
}
packet.get_layer(LayerType::Tcp);

fn get_layer<T>(&self, T) -> Option<&Layer> {
    ...
}
packet.get_layer::<Layer::Tcp>();

Thanks!

The first solution could be to use macros, eg something like this (not tested):

macro_rules! get_layer {
    ( $( $discriminant:tt)* ) => {
        self.layers.iter().find(|l| matches!(l, $($discriminant)* (_))) 
    }
}

fn get_tcp_layer(&self) -> Option<&Layer> {
    get_layer!(Layer::Tcp)
}

Another solution would be to use std::mem::discriminant (not tested either):

use std::mem;

fn get_layer<T>(&self, discriminant: mem::Discriminant<Layer>) -> Option<&Layer> {
    self.layers.iter().find(|l| mem::discriminant(l) == discriminant)
}

fn get_tcp_layer(&self) -> Option<&Layer> {
    let tcp = Layer::Tcp(TcpHeader::new());
    self.get_layer(mem::discriminant(&tcp))
}
2 Likes

Thanks! That's really help.