4

I'd like to serialize a struct in two different ways depending of the situation but I'm facing a problem: with my current knowledge I can only serialize the struct in one way.

Here is my code with #[derive(Serialize)] (auto derive)

#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct TransactionContent {
    sender_addr: Vec<u8>,
    sender_pubkey: Vec<u8>,
    receiver_addr: Vec<u8>,
    amount: u32,
    timestamp: i64
}

I'm using bincode::serialize to serialize my struct and make it a Vec<u8> and I also want to store that struct in a JSON file. When serializing to JSON, I'd like to serialize it in my own way, like returning a base58 string for the Vec<u8> fields.

This is my own implementation:

impl Serialize for TransactionContent {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let mut state = serializer.serialize_struct("TransactionContent", 5)?;
        state.serialize_field("sender_addr", &self.sender_addr.to_base58())?;
        state.serialize_field("sender_pubkey", &self.sender_pubkey.to_base58())?;
        state.serialize_field("receiver_addr", &self.receiver_addr.to_base58())?;
        state.serialize_field("amount", &self.amount)?;
        state.serialize_field("timestamp", &self.timestamp)?;
        state.end()
    }
}

I can't use the above code simultaneously. If I use the auto derive, the second Impl isn't possible. If I use the second one, the bincode::serialize function will work but not as I want it to (I want to use Vec<u8> for it)

Is there a way that I could use both Impl at the same time? Something like a conditional Impl for example?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
mgul
  • 742
  • 8
  • 27
  • 5
    You can implement different serialization for wrapper `struct TransactionContentBase58(TransactionContent);`. Will it solve your problem? – red75prime Sep 12 '17 at 06:00

1 Answers1

8

No, you cannot implement the same trait multiple times in multiple ways for a single type.

As mentioned in a comment, you can create a newtype that wraps the full data and implement the required traits on that:

use serde::{ser::SerializeStruct, Serialize, Serializer}; // 1.0.117
use serde_json; // 1.0.59

#[derive(Debug, Serialize)]
struct Real {
    data: Vec<u8>,
}

struct AsJson<'a>(&'a Real);

impl<'a> Serialize for AsJson<'a> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut state = serializer.serialize_struct("Thing", 1)?;
        state.serialize_field("data", b"this is some data")?;
        state.end()
    }
}

fn main() {
    let r = Real {
        data: vec![1, 2, 3, 4],
    };
    println!("{:?}", serde_json::to_string(&r));
    println!("{:?}", serde_json::to_string(&AsJson(&r)));
}

If you control the trait

You could add a generic parameter to the trait and implement it multiple times for the same type:

trait Example<T> {}

struct Style1;
struct Style2;

impl Example<Style1> for i32 {}
impl Example<Style2> for i32 {}

This isn't without downsides though.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366