Reputation: 43
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)?
Upvotes: 2
Views: 11441
Reputation: 2581
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
}
Upvotes: 6
Reputation: 432
You can make deserialize
generic over the Deserialize
trait:
fn deserialize<'a, T: Deserialize<'a>>(data: &'a [u8]) -> T {
let msg = str::from_utf8(data).unwrap();
serde_json::from_str(msg).unwrap()
}
note that you need some lifetimes, because some types needs to borrow from the deserialized string, doc.
You can make serialize
generic too:
fn serialize<T: Serialize>(object: &T) -> String {
serde_json::to_string(object).unwrap()
}
Upvotes: 2