Handwritten generic type bounds
When deriving Serialize
and Deserialize
implementations for structs with
generic type parameters, most of the time Serde is able to infer the correct
trait bounds without help from the programmer. It uses several heuristics to
guess the right bound, but most importantly it puts a bound of T: Serialize
on
every type parameter T
that is part of a serialized field and a bound of T:
Deserialize
on every type parameter T
that is part of a deserialized field.
As with most heuristics, this is not always right and Serde provides an escape
hatch to replace the automatically generated bound by one written by the
programmer.
use serde::{de, Deserialize, Deserializer};
use std::fmt::Display;
use std::str::FromStr;
#[derive(Deserialize, Debug)]
struct Outer<'a, S, T: 'a + ?Sized> {
// When deriving the Deserialize impl, Serde would want to generate a bound
// `S: Deserialize` on the type of this field. But we are going to use the
// type's `FromStr` impl instead of its `Deserialize` impl by going through
// `deserialize_from_str`, so we override the automatically generated bound
// by the one required for `deserialize_from_str`.
#[serde(deserialize_with = "deserialize_from_str")]
#[serde(bound(deserialize = "S: FromStr, S::Err: Display"))]
s: S,
// Here Serde would want to generate a bound `T: Deserialize`. That is a
// stricter condition than is necessary. In fact, the `main` function below
// uses T=str which does not implement Deserialize. We override the
// automatically generated bound by a looser one.
#[serde(bound(deserialize = "Ptr<'a, T>: Deserialize<'de>"))]
ptr: Ptr<'a, T>,
}
/// Deserialize a type `S` by deserializing a string, then using the `FromStr`
/// impl of `S` to create the result. The generic type `S` is not required to
/// implement `Deserialize`.
fn deserialize_from_str<'de, S, D>(deserializer: D) -> Result<S, D::Error>
where
S: FromStr,
S::Err: Display,
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
S::from_str(&s).map_err(de::Error::custom)
}
/// A pointer to `T` which may or may not own the data. When deserializing we
/// always want to produce owned data.
#[derive(Debug)]
enum Ptr<'a, T: 'a + ?Sized> {
Ref(&'a T),
Owned(Box<T>),
}
impl<'de, 'a, T: 'a + ?Sized> Deserialize<'de> for Ptr<'a, T>
where
Box<T>: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Deserialize::deserialize(deserializer).map(Ptr::Owned)
}
}
fn main() {
let j = r#"
{
"s": "1234567890",
"ptr": "owned"
}
"#;
let result: Outer<u64, str> = serde_json::from_str(j).unwrap();
// result = Outer { s: 1234567890, ptr: Owned("owned") }
println!("result = {:?}", result);
}