Serde Deserialize and lifetimes confusion

Consider the following Trait

pub trait Thing: Deserialize + Serialize {}

I am using it like this.

#[derive(Deserialize, Serialize)]
pub struct Listing<T: Thing> {
    children: Vec<T>,
}

But this gives me an error saying that pub trait Thing: Deserialize + Serialize {} needs a lifetime annotation.

After reading up on Deserializer lifetimes · Serde I tried changing Deserialize to DeserializeOwned.

pub trait Thing: DeserializeOwned + Serialize {}

But then I can no longer derive Deserialize. It says

4 | #[derive(Deserialize, Serialize)]
  |          ^^^^^^^^^^^ cannot infer type for type parameter `T`

How do I solve this? Why can't I derive Deserialize if I change the trait bound to DeserializeOwned ?

Try just leaving off the trait bounds on the struct. You don't need them for the struct to be deserializable — the derived impl will have appropriate bounds.

1 Like

The Deserialize trait has a lifetime parameter attached to it Deserialize<'de>. It is not optional, which is why you see the first error.

The second form using DeserializeOwned should work as you have written, but fails due to a bug in the rust compiler Inference disrupted by trait bound that is redundant with a HRTB · Issue #41617 · rust-lang/rust · GitHub. There are some more details and workarounds in the corresponding serde issue Unable to derive deserialize on generic type, where generic parameter is bounded by DeserializeOwned · Issue #1296 · serde-rs/serde · GitHub.

While using DeserializeOwned should work, it might also be too restrictive, in that it prohibits any borrowing during deserialization. You are most flexible if you remove the trait bounds (as @kpreid already said), which is also generally recommended for derivable traits.

2 Likes

Ah, thanks for the detailed explanation. I don't technically need the trait bounds, but just thought logically it was useful because I want Listing<T> to be applicable only to those implementing the Thing trait since they are API endpoints. I don't want someone initializing it as Listing<int> for example.