Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deriving Serde's Serialize or Deserialize forces generic type to be serialisable although it does not need to be

Tags:

rust

serde

My type A, which can contain anything that implements trait Trait, is serialisable, although the type implementing the trait Trait might not be. In my case, it cannot be - it's a private asymmetric key:

extern crate serde;
#[macro_use]
extern crate serde_derive;

use serde::de::DeserializeOwned;
use serde::Serialize;

trait Trait {
    type SerialisableType: Clone + Serialize + DeserializeOwned;

    fn inner(&self) -> &Self::SerialisableType;
}

#[derive(Serialize, Deserialize)]
enum A<T: Trait> {
    Variant0(B<T>), // *** NOTE: Compiles if this is commented ***
    Variant1(T::SerialisableType),
}

#[derive(Serialize, Deserialize)]
struct B<T: Trait> {
    inner: T::SerialisableType,
}

// ==============================================

struct NonSerialisable {
    serialisable: Serialisable,
}

impl Trait for NonSerialisable {
    type SerialisableType = Serialisable;

    fn inner(&self) -> &Self::SerialisableType {
        &self.serialisable
    }
}

#[derive(Clone, Serialize, Deserialize)]
struct Serialisable(Vec<u8>);

#[derive(Serialize, Deserialize)]
enum E {
    Variant0(A<NonSerialisable>),
    Variant1(B<NonSerialisable>),
}

fn main() {}

playground

This errors out with:

error[E0277]: the trait bound `NonSerialisable: serde::Serialize` is not satisfied
  --> src/main.rs:43:10
   |
43 | #[derive(Serialize, Deserialize)]
   |          ^^^^^^^^^ the trait `serde::Serialize` is not implemented for `NonSerialisable`
   |
   = note: required because of the requirements on the impl of `serde::Serialize` for `A<NonSerialisable>`
   = note: required by `serde::Serializer::serialize_newtype_variant`

error[E0277]: the trait bound `NonSerialisable: serde::Deserialize<'_>` is not satisfied
  --> src/main.rs:43:21
   |
43 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^ the trait `serde::Deserialize<'_>` is not implemented for `NonSerialisable`
   |
   = note: required because of the requirements on the impl of `serde::Deserialize<'_>` for `A<NonSerialisable>`
   = note: required by `serde::de::VariantAccess::newtype_variant`

If I comment out A::Variant0, as mentioned in the inline comment in the code, then it compiles fine. This makes me think that the compiler is unable to deduce that B<T> is serialisable, but then it actually is able to deduce that because it can figure out E is serialisable which would require B to be serialisable as well.

Where is the problem?

like image 201
ustulation Avatar asked Apr 26 '18 15:04

ustulation


1 Answers

During macro expansion the compiler has not yet determined which B is being referred to inside Variant0 or how that B may use its type parameters. As such, macro expansion infers trait bounds that would work for the most common cases of what B might be, like if B were Box or Vec. In those cases serializing B<T> would require T: Serialize and deserializing B<T> would require T: Deserialize<'de>.

You can provide handwritten generic type bounds to replace the inferred bounds.

#[derive(Serialize, Deserialize)]
#[serde(bound = "")]
enum A<T: Trait> {
    Variant0(B<T>),
    Variant1(T::SerialisableType),
}
like image 129
dtolnay Avatar answered Sep 21 '22 00:09

dtolnay