Deserialize as a trait bound

How can I accomplish this? This doesn't compile and is this something that is achievable. What am I missing here?


#[derive(Serialize, Deserialize)]
struct Test<'a, T>
    where T : Serialize + Deserialize<'a>
{
    t_ : T,
    marker_ : PhantomData<&'a T>
}

impl<'a, T> Test<'a, T>
    where T : Serialize + Deserialize<'a>
{
    fn new(t : T) -> Self
    {
        return Self {
            t_ : t,
            marker_ : PhantomData
        };
    }
}

These are the errors I get and can't figure out how to make progress.

error[E0283]: type annotations needed: cannot satisfy `T: Deserialize<'a>`
 --> src/lib.rs:6:27
  |
6 |     where T : Serialize + Deserialize<'a>
  |                           ^^^^^^^^^^^^^^^
  |
  = note: cannot satisfy `T: Deserialize<'a>`

error[E0283]: type annotations needed: cannot satisfy `T: Deserialize<'a>`
 --> src/lib.rs:5:8
  |
5 | struct Test<'a, T>
  |        ^^^^^^^^^^^
  |
  = note: cannot satisfy `T: Deserialize<'a>`
note: required by a bound in `Test`
 --> src/lib.rs:6:27
  |
5 | struct Test<'a, T>
  |        ---- required by a bound in this
6 |     where T : Serialize + Deserialize<'a>
  |                           ^^^^^^^^^^^^^^^ required by this bound in `Test`

error[E0283]: type annotations needed: cannot satisfy `T: Deserialize<'_>`
 --> src/lib.rs:4:21
  |
4 | #[derive(Serialize, Deserialize)]
  |                     ^^^^^^^^^^^
  |
  = note: cannot satisfy `T: Deserialize<'_>`
note: required for `__Visitor<'de, 'a, T>` to implement `Visitor<'de>`
 --> src/lib.rs:4:21
  |
4 | #[derive(Serialize, Deserialize)]
  |                     ^^^^^^^^^^^
  = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0283`.

struct Test is just bare bones and can have more fields. But the key is that needs to be generic enough to hold a type T that can be serialized and deserialized.

The where bound on the struct is confusing the derive. If you look at the derive output (Tools > Expand macros), after removing the where clause, you end up with a bound like

        impl<'de, 'a, T> _serde::Deserialize<'de> for Test<'a, T> where
            T: _serde::Deserialize<'de> {

So you don't really need the bound for the derive to work. (Also as we don't have implied bounds, you have to repeat these bounds on T everywhere you need them anyway, even if you put them on the struct.)

The docs also imply you should add the serde(borrow) attribute, after which the implementation is

        impl<'de: 'a, 'a, T> _serde::Deserialize<'de> for Test<'a, T> where
            T: _serde::Deserialize<'de> {

Note the extra 'de: 'a bound. This might matter in practice due to trait parameters being invariant.


Everything above assumed you explicitly don't want to require DeserializeOwned and have to deal with potentially lifetime-limited, yet opaque, T.

In short, applying a trait bound to the struct definition is an antipattern. You should only be adding trait bounds to the implementations which require them.

There are cases where a trait bound is structural, in that the fields' types rely on the trait bound, such as by using associated types of the trait. In this case alone, it makes sense to bound the generics on the struct definition.

If you're putting the bound in order to satisfy derives — don't. Derives already introduce the standardly required bounds on the generic type parameters. If you need bounds beyond the default for some reason, you can utilize attributes like #[serde(bound = "T: Trait")] to introduce them.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.