Serde serialization implementation for c-like enums

Hi all,

I'm trying to build an example of a custom serde serialization using what is explained here: Implementing a Serializer · Serde.

For structs, it works perfectly. But for C-like enums, I'm wondering how to get the enum value ? The serialize_unit_variant method is only passing the enum name, index and variant name. Not its value.

Any idea ?

Thanks for your help.

serialize_unit_variant is for serialising enum variants without values. If a variant has values then you want serialize_struct_variant or serialize_tuple_variant, depending on whether the values are named.

C-like enums don't normally store values though. If you mean you want to serialise the value the enum variant corresponds to rather than storing it by name, then you probably want to serialise an integer instead and do the conversion in the Serialize implementation. You could also vary it based on the result of is_human_readable if you want to support both text and integers.

2 Likes

Maybe your looking for this: Enumerations - The Rust Reference

If an enumeration is unit-only, then its discriminant can be directly accessed with a numeric cast.

#[repr(u8)]
enum Foo {
    A,
    B,
}

// ...
Foo::A as u8

There are crates that help going the other direction, but you can manually implement it as well.

2 Likes

Thanks guys four your replies.

For one of my personal project (GitHub - dandyvica/dqy: A DNS query tool), I developed my own crate to ser/deser data structures (mainly C-like enums and structs) to bytes in network order (GitHub - dandyvica/type2network: Traits and procedural macros to convert structures and enums to data streams). For education purposes mainly, and also because I found the bincode crate very cumbersome to use.

Now, I think its time to come back to serde to do this.

You're right, I want the value of the enum variant. I have access to the name but not the value. The methods you're referring to seem dedicated to what their name hints toward, not unit variants.

I actually made it work for a single C-like enum with a trick but defining a trait which gives the C-like enum value.

pub trait Extract {
    fn extract(&self) -> u64;
}

#[derive(Debug, Serialize, Clone, Copy)]
#[repr(u16)]
enum Color {
    Red = 0xFF,
    Green = 9 * 3,
    Blue = 9 * 4,
}

impl Extract for Color {
    fn extract(&self) -> u64 {
        return *self as u64;
    }
}

#[derive(Debug)]
pub struct MySerializer {
    // length of data being serialized
    length: usize,

    // resulting buffer where data are copied
    buffer: Vec<u8>,
    size: usize,
    extracted: u64,
}

pub fn to_network<T: Extract>(value: &T) -> Result<Vec<u8>>
where
    T: Serialize,
  
{
    //println!("value of T: {:?}", value);
    let extracted = value.extract();
    let mut serializer = MySerializer {
        length: 0,
        buffer: Vec::new(),
        size: std::mem::size_of::<T>(),
        extracted: extracted,
    };
    value.serialize(&mut serializer)?;
    Ok(serializer.buffer)
}
...
    fn serialize_unit_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
    ) -> Result<usize> {
        println!(
            "serializing enum named: {}, variant named: {}, variant index: {}",
            _name, variant, _variant_index
        );
        match self.size {
            // enum was repr(u8)
            1 => self.serialize_u8(self.extracted as u8),
            // enum was repr(u16)
            2 => self.serialize_u16(self.extracted as u16),
            // enum was repr(u32)
            4 => self.serialize_u32(self.extracted as u32),
            // enum was repr(u64)
            8 => self.serialize_u64(self.extracted),
            _ => unimplemented!("value {} is not implemented", self.size ),
        }
        // self.serialize_str(variant)
    }
...


This way it works. But whenever the enum is inside a struct, it doesn't.

Serde only gives serializers two options for[1] enum variants: name or index. That's hardcoded into the serde data model. If you want it to serialize as an integer, you need to change the enum's implementation of Serialize. You can use serde_repr for this.

#[derive(Debug, Serialize_repr, Clone, Copy)]
#[repr(u16)]
enum Color {
    Red = 0xFF,
    Green = 9 * 3,
    Blue = 9 * 4,
}

  1. things that reach the serializer as ↩︎

3 Likes

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.