Search code examples
rustserde

How to properly serialize/deserialize rust?


I have multiple structs that correspond to serialized/deserialized objects only known during runtime, example:

#[derive(Serialize, Deserialize)]
struct Car{
    model: i32,
    year: i32
}

#[derive(Serialize, Deserialize)]
struct Person{
    name: String,
    age: i32
}

Then I have functions to serialize and deserialize:

fn deserialize(data: Vec<u8>){
    let msg = str::from_utf8(data);
    serde_json::from_str(msg);
}

fn serialize(&self, object: Car) -> String{
    let msg = serde_json::to_string(&object).unwrap();
    return msg;
}

How can I make the deserialize function deserialize to both Car and Person (and possibly many other different types) and return the object? And how can I make the serialize function do the same thing: serialize Car, Person, and other objects (accepting these types in the attributes of the function)?


Solution

  • You want to use generic functions to allow different types to be passed in, and set trait bounds to make sure the objects are able to be serialized/deserialized. When calling serialize, the type will be inferred by the type of the parameter, but when calling deserialize, you need to use the turbofish (::<>) to specify the type, if it can't be inferred.

    use serde::{Serialize, Deserialize};
    use std::str;
    
    #[derive(Serialize, Deserialize)]
    struct Car {
        model: i32,
        year: i32
    }
    
    #[derive(Serialize, Deserialize)]
    struct Person {
        name: String,
        age: i32
    }
    
    // constrain output types to have the `Deserialize` trait
    fn deserialize<'a, T>(data: &'a [u8]) -> T where T: Deserialize<'a> {
        let msg = str::from_utf8(data).unwrap();
        serde_json::from_str::<T>(msg).unwrap()
    }
    
    // shorthand for the above when `T` isn't needed in the function body
    fn serialize(object: &impl Serialize) -> String {
        let msg = serde_json::to_string(object).unwrap();
        return msg;
    }
    
    fn main() {
        let car = Car { model: 7, year: 2077 };
        let person = Person { name: "Bob".to_string(), age: 42 };
    
        // types are infrerred from the parameters
        let car_json = serialize(&car);
        let person_json = serialize(&person);
    
        let _: Car = deserialize(car_json.as_bytes()); // output type can be inferred
        let _ = deserialize::<Car>(car_json.as_bytes()); // requres turbofish
    
        let _: Person = deserialize(person_json.as_bytes()); // works for `Person` too
    }