Derive Macro Deserialize

Source
#[derive(Deserialize)]
{
    // Attributes available to this derive:
    #[serde]
    #[eserde]
}
Expand description

A derive macro to automatically implement EDeserialize and serde::Deserialize for a type.

§Example

//                         👇 `eserde`'s derive
#[derive(serde::Serialize, eserde::Deserialize)]
struct Point {
    x: f64,
    y: f64,
}

§Common issues

Using both #[derive(eserde::Deserialize)] and #[derive(serde::Deserialize)] on the same type will cause a compilation error:

// 🚫 Won't compile
#[derive(eserde::Deserialize, serde::Deserialize)]
struct Point {
    x: f64,
    y: f64,
}

The compilation error is a variation of the following:

error[E0119]: conflicting implementations of trait `Deserialize<'_>` for type `Point`
--> my_module.rs
 |
 | #[derive(eserde::Deserialize, serde::Deserialize)]
 |          -------------------  ^^^^^^^^^^^^^^^^^^ conflicting implementation for `Point`
 |          |
 |          first implementation here
 |
 = note: this error originates in the derive macro `serde::Deserialize`

§Customizing the deserialization process

You can use serde attributes to control how your type is deserialized.

#[derive(eserde::Deserialize)]
struct Point {
    #[serde(rename = "latitude")]
    x: f64,
    #[serde(rename = "longitude")]
    y: f64,
}

eserde will pick up those attributes and honor their contracts.

§Interoperability

eserde::Deserialize expects all fields in your type to implement eserde::EDeserialize. If that’s not the case, there will be a compilation error.

Consider this example:

#[derive(eserde::Deserialize)]
struct Point {
    x: Latitude,
    y: Longitude,
}

//      👇 Plain serde!
#[derive(serde::Deserialize)]
struct Latitude(f64);

#[derive(eserde::Deserialize)]
struct Longitude(f64);

It’ll fail to compile with the following error:

error[E0277]: the trait bound `Latitude: EDeserialize<'_>` is not satisfied
|
| #[derive(eserde::Deserialize)]
|          ^^^^^^^^^^^^^^^^^^^
| the trait `EDeserialize<'_>` is not implemented for `Latitude`

If Latitude is defined in one of your crates, you can fix the issue by replacing #[derive(serde::Deserialize)] with #[derive(eserde::Deserialize)] on its definition.
That may not always be possible, though—e.g. Latitude may come from a third-party crate that doesn’t support eserde yet.
You can bypass the issue using the #[eserde(compat)] attribute:

#[derive(eserde::Deserialize)]
struct Point {
    // 👇 New attribute!
    #[eserde(compat)]
    x: Latitude,
    y: Longitude,
}

#[derive(serde::Deserialize)]
struct Latitude(f64);

#[derive(eserde::Deserialize)]
struct Longitude(f64);

#[eserde(compat)] instructs eserde to use serde’s deserialization logic for Latitude when collecting errors. It now compiles!
There’s a catch, though: you’ll get at most one deserialization error from a field annotated with #[eserde(compat)], since we can’t rely on EDeserialize’s error machinery.

§Limitations

eserde doesn’t support all serde attributes (yet).

The following container attributes will be rejected at compile-time:

  • #[serde(untagged)] on enums
  • #[serde(default)]
  • #[serde(remote = "...")]
  • #[serde(try_from = "...")]
  • #[serde(from = "...")]
  • #[serde(bound = "...")]
  • #[serde(variant_identifier)]
  • #[serde(field_identifier)]

The following variant attributes will be rejected at compile-time:

  • #[serde(skip_deserializing)]
  • #[serde(deserialize_with = "...")]
  • #[serde(with = "...")]
  • #[serde(bound = "...")]
  • #[serde(untagged)]

The following field attributes will be rejected at compile-time:

  • #[serde(default = "...")] if it doesn’t rely on Default::default for the default logic
  • #[serde(skip_deserializing)]
  • #[serde(deserialize_with = "...")]
  • #[serde(with = "...")]
  • #[serde(bound = "...")]

We plan to support most of these attributes in the future.