jonasbb / serde_with

This crate provides custom de/serialization helpers to use in combination with serde's `with`-annotation and with the improved `serde_as`-annotation.
https://docs.rs/serde_with
Apache License 2.0
636 stars 67 forks source link

DurationSeconds w/ chrono TimeDelta errors `Deserialize not implemented` #758

Closed docwilco closed 2 months ago

docwilco commented 2 months ago

When trying to use DurationSeconds with chrono I get:

error[E0277]: the trait bound `TimeDelta: Deserialize<'_>` is not satisfied
    --> src/main.rs:9:9
     |
9    |     d1: Duration,
     |         ^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `TimeDelta`
     |
     = help: the following other types implement trait `Deserialize<'de>`:
               &'a Path
               &'a [u8]
               &'a str
               ()
               (T,)
               (T0, T1)
               (T0, T1, T2)
               (T0, T1, T2, T3)
             and 150 others
note: required by a bound in `next_element`
    --> C:\Users\msnpa\.cargo\registry\src\index.crates.io-6f17d22bba15001f\serde-1.0.203\src\de\mod.rs:1726:12
     |
1724 |     fn next_element<T>(&mut self) -> Result<Option<T>, Self::Error>
     |        ------------ required by a bound in this associated function
1725 |     where
1726 |         T: Deserialize<'de>,
     |            ^^^^^^^^^^^^^^^^ required by this bound in `SeqAccess::next_element`

error[E0277]: the trait bound `TimeDelta: Deserialize<'_>` is not satisfied
    --> src/main.rs:11:9
     |
11   |     d2: TimeDelta,
     |         ^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `TimeDelta`
     |
     = help: the following other types implement trait `Deserialize<'de>`:
               &'a Path
               &'a [u8]
               &'a str
               ()
               (T,)
               (T0, T1)
               (T0, T1, T2)
               (T0, T1, T2, T3)
             and 150 others
note: required by a bound in `next_element`
    --> C:\Users\msnpa\.cargo\registry\src\index.crates.io-6f17d22bba15001f\serde-1.0.203\src\de\mod.rs:1726:12
     |
1724 |     fn next_element<T>(&mut self) -> Result<Option<T>, Self::Error>
     |        ------------ required by a bound in this associated function
1725 |     where
1726 |         T: Deserialize<'de>,
     |            ^^^^^^^^^^^^^^^^ required by this bound in `SeqAccess::next_element`

error[E0277]: the trait bound `TimeDelta: Deserialize<'_>` is not satisfied
    --> src/main.rs:9:9
     |
9    |     d1: Duration,
     |         ^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `TimeDelta`
     |
     = help: the following other types implement trait `Deserialize<'de>`:
               &'a Path
               &'a [u8]
               &'a str
               ()
               (T,)
               (T0, T1)
               (T0, T1, T2)
               (T0, T1, T2, T3)
             and 150 others
note: required by a bound in `next_value`
    --> C:\Users\msnpa\.cargo\registry\src\index.crates.io-6f17d22bba15001f\serde-1.0.203\src\de\mod.rs:1865:12
     |
1863 |     fn next_value<V>(&mut self) -> Result<V, Self::Error>
     |        ---------- required by a bound in this associated function
1864 |     where
1865 |         V: Deserialize<'de>,
     |            ^^^^^^^^^^^^^^^^ required by this bound in `MapAccess::next_value`

error[E0277]: the trait bound `TimeDelta: Deserialize<'_>` is not satisfied
    --> src/main.rs:11:9
     |
11   |     d2: TimeDelta,
     |         ^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `TimeDelta`
     |
     = help: the following other types implement trait `Deserialize<'de>`:
               &'a Path
               &'a [u8]
               &'a str
               ()
               (T,)
               (T0, T1)
               (T0, T1, T2)
               (T0, T1, T2, T3)
             and 150 others
note: required by a bound in `next_value`
    --> C:\Users\msnpa\.cargo\registry\src\index.crates.io-6f17d22bba15001f\serde-1.0.203\src\de\mod.rs:1865:12
     |
1863 |     fn next_value<V>(&mut self) -> Result<V, Self::Error>
     |        ---------- required by a bound in this associated function
1864 |     where
1865 |         V: Deserialize<'de>,
     |            ^^^^^^^^^^^^^^^^ required by this bound in `MapAccess::next_value`

error[E0277]: the trait bound `TimeDelta: Deserialize<'_>` is not satisfied
  --> src/main.rs:5:10
   |
5  | #[derive(Deserialize)]
   |          ^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `TimeDelta`
   |
   = help: the following other types implement trait `Deserialize<'de>`:
             &'a Path
             &'a [u8]
             &'a str
             ()
             (T,)
             (T0, T1)
             (T0, T1, T2)
             (T0, T1, T2, T3)
           and 150 others
note: required by a bound in `_serde::__private::de::missing_field`
  --> C:\Users\msnpa\.cargo\registry\src\index.crates.io-6f17d22bba15001f\serde-1.0.203\src\private\de.rs:25:8
   |
23 | pub fn missing_field<'de, V, E>(field: &'static str) -> Result<V, E>
   |        ------------- required by a bound in this function
24 | where
25 |     V: Deserialize<'de>,
   |        ^^^^^^^^^^^^^^^^ required by this bound in `missing_field`
   = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

source for that:

use chrono::{Duration, TimeDelta};
use serde::Deserialize;
use serde_with::serde_as;

#[derive(Deserialize)]
#[serde_as]
struct Test {
    #[serde_as(as = "DurationSeconds<i64>")]
    d1: Duration,
    #[serde_as(as = "DurationSeconds<i64>")]
    d2: TimeDelta,
}

fn main() {}

And Cargo.toml:

[package]
name = "foo"
version = "0.1.0"
edition = "2021"

[dependencies]
chrono = { version = "0.4.38", features = ["serde"] }
serde = { version = "1.0.203", features = ["derive"] }
serde_with = { version = "3.8.1", features = ["chrono","chrono_0_4"] }
jonasbb commented 2 months ago

You need to switch the order of the macro annotations around. serde_as must always come before the derive. The reason is that the macro order is significant in Rust, as the macros are expanded top down. You need to place serde_as first as it must run before the derive generates all its code, as otherwise the derive will not know about the changed behavior. That should fix everything.

#[serde_as]
#[derive(Deserialize)]
docwilco commented 2 months ago

And that was indeed the thing. Any way of making the compiler point that out?

jonasbb commented 2 months ago

As far as I know this is not possible. The proc-macro cannot tell that the serde derives are "missing" (or already executed). At least not reliably. A warning cannot be printed, since there is no stable API to perform that for a proc-macro, see https://github.com/rust-lang/rust/issues/54140. The only option is a full compile_error!.