Below is a minimalized example of my problem. I have a Struct that has multiple Algorithm choices implemented by traits. I can serialize easily enough into a simple tuple of (algo_name, value), but I'm having difficulty coming up with a way to universally deserialize that string tuple into it's appropriate Algorithm bound Struct.
The implementation I've got below works fine when I already know the bound trait to use. I get it.. that's how everything is supposed to work, hooray!
But how do I choose an Algorithm from it's string representation? How do I map strings to Structs in a way that doesn't blow up trait bounds? I'm learning that Rust has a pretty strong wall between generics/bounds and values, and I fear I may have coded myself into a very nasty corner.
The options I see so far are:
- refactor the Algorithm trait into a big nasty enum, which in my case would be a pretty big rewrite I would like to avoid.
- write a custom, unattached to the Deserialize trait, function that does some sort of peek inside the serialized string before deserializing.
I'm hoping there's an elegant, idiomatic solution I'm not seeing.
use std::marker::PhantomData;
use serde::{Deserialize, Deserializer, Serialize, Serializer, ser::SerializeTuple, de::{SeqAccess, Visitor, Error}};
use serde_json::json; // 1.0.85
#[derive(Debug)] struct AlgoOne;
#[derive(Debug)] struct AlgoTwo;
trait Algorithm {
const ALGORITHM: &'static str;
}
impl Algorithm for AlgoOne {
const ALGORITHM: &'static str = "AlgoOne";
}
impl Algorithm for AlgoTwo {
const ALGORITHM: &'static str = "AlgoTwo";
}
#[derive(Debug)]
struct Signature<T> {
algo: PhantomData<T>,
data: String
}
impl<T> Signature<T> where T: Algorithm {
fn new(data: String) -> Self {
Self {
algo: PhantomData,
data
}
}
fn algorithm(&self) -> &'static str {
T::ALGORITHM
}
}
impl<T> Serialize for Signature<T> where T: Algorithm {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
let a = self.algorithm();
let mut s = serializer.serialize_tuple(2)?;
s.serialize_element(a)?;
s.serialize_element(&self.data)?;
s.end()
}
}
struct SignatureVisitor;
impl<'de> Visitor<'de> for SignatureVisitor {
type Value = (&'de str, &'de str);
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a tuple of two strings")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let l: &'de str = seq.next_element()?.unwrap();
let r: &'de str = seq.next_element()?.unwrap();
Ok((l, r))
}
}
impl<'de, T> Deserialize<'de> for Signature<T>
where
T: Algorithm,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let g = deserializer.deserialize_tuple(2, SignatureVisitor)?;
if g.0 != T::ALGORITHM {
Err(Error::custom("Invalid Tuple Type"))
} else {
Ok(Self::new(g.1.to_string()))
}
}
}
fn main() {
let a: Signature<AlgoOne> = Signature::new("steve".to_owned());
let j = serde_json::to_string(&a).unwrap();
dbg!(j); // ["AlgoOne","steve"]
let k = json!(("AlgoTwo", "horace")).to_string();
dbg!(&k); // ["AlgoTwo","horace"]
let r: Result<Signature<AlgoTwo>, serde_json::Error> = serde_json::from_str(&k);
match r {
Err(e) => println!("Error: {}", e.to_string()),
Ok(r) => println!("{:?}", r),
}
}