Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deserialization doesn't work on hashmap if both generic parameters are constrainted with DeserializeAs types #582

Closed
Pzixel opened this issue Mar 29, 2023 · 3 comments

Comments

@Pzixel
Copy link

Pzixel commented Mar 29, 2023

Consider following code:

use serde::{Deserialize, Deserializer, Serialize};
use serde_with::serde_as;
use serde_with::DeserializeAs;
use std::collections::HashMap;

#[derive(Serialize, Deserialize)]
struct Test(i32);

impl<'de> DeserializeAs<'de, i32> for Test {
    fn deserialize_as<D>(deserializer: D) -> Result<i32, D::Error>
    where
        D: Deserializer<'de>,
    {
        let result = Self::deserialize(deserializer)?;
        Ok(result.0)
    }
}

#[serde_as]
#[derive(Deserialize)]
struct Foo {
    #[serde_as(deserialize_as = "HashMap<i32, Test>")]
    pub bar: HashMap<i32, i32>,
}

It fails with cryptic message:

error[E0277]: the trait bound `HashMap<i32, Test>: DeserializeAs<'_, _>` is not satisfied
   --> src/lib.rs:19:1
    |
19  | #[serde_as]
    | ^^^^^^^^^^^ the trait `DeserializeAs<'_, _>` is not implemented for `HashMap<i32, Test>`
    |
    = help: the following other types implement trait `DeserializeAs<'de, T>`:
              <HashMap<KAs, VAs> as DeserializeAs<'de, BTreeSet<(K, V)>>>
              <HashMap<KAs, VAs> as DeserializeAs<'de, BinaryHeap<(K, V)>>>
              <HashMap<KAs, VAs> as DeserializeAs<'de, HashSet<(K, V)>>>
              <HashMap<KAs, VAs> as DeserializeAs<'de, LinkedList<(K, V)>>>
              <HashMap<KAs, VAs> as DeserializeAs<'de, Vec<(K, V)>>>
              <HashMap<KAs, VAs> as DeserializeAs<'de, VecDeque<(K, V)>>>
              <HashMap<KAs, VAs> as DeserializeAs<'de, [(K, V); N]>>
              <HashMap<KAs, VAs> as DeserializeAs<'de, std::option::Option<(K, V)>>>
              <HashMap<KU, VU, S> as DeserializeAs<'de, HashMap<K, V, S>>>
note: required by a bound in `As::<T>::deserialize`
   --> /Users/pzixel/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_with-1.14.0/src/lib.rs:442:12
    |
442 |         T: DeserializeAs<'de, I>,
    |            ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `As::<T>::deserialize`
    = note: this error originates in the attribute macro `serde_as` (in Nightly builds, run with -Z macro-backtrace for more info)

Note the line <HashMap<KU, VU, S> as DeserializeAs<'de, HashMap<K, V, S>>>.
So if we constraint both hashmap args with Test:

#[serde_as]
#[derive(Deserialize)]
struct Foo {
    #[serde_as(deserialize_as = "HashMap<Test, Test>")]
    pub bar: HashMap<i32, i32>,
}

Then it gets compiled as expected. For now I implemented a thin wrapper that is only used to fool this constraint but I think it's overly conservative. Users definitely should be able to deserialize HashMap<SomeStandardType, SomeWrapperType>

@Pzixel
Copy link
Author

Pzixel commented Mar 29, 2023

Seems like duplicate a to #202 . Would be very happy to have this implemented. Least effort solution may be implementing SerdeIdproxy type and documenting its usage in docs.

@jonasbb
Copy link
Owner

jonasbb commented Mar 29, 2023

The correct attribute is #[serde_as(deserialize_as = "HashMap<_, Test>")], with _ indicating that you want to use the Deserialize implementation of the type.
Otherwise, yes that is a duplicate of #202. PRs are welcome. I am however not aware of any way the current trait system could be used to implement this.

I don't know what your suggested SerdeId is supposed to do. Is it identical to serde_with::Same?

@jonasbb jonasbb closed this as not planned Won't fix, can't repro, duplicate, stale Mar 29, 2023
@Pzixel
Copy link
Author

Pzixel commented Mar 29, 2023

Yes, I believe Same is what I was looking for. I wasn't also aware of _ syntax, I think docs would benefit from explicitly stating what is it and how to use it. Now when you explained it I can see them in arrays section but it's not particularly obvious so I think having a specific section about it would be nice.

jonasbb added a commit that referenced this issue Apr 1, 2023
Mention that it is supposed to mirror the type structure of the field
and what the special `_` placeholder means.

This was mentioned as insufficiently explained in #582.
bors bot added a commit that referenced this issue Apr 1, 2023
583: Explain better how to use serde_as r=jonasbb a=jonasbb

Mention that it is supposed to mirror the type structure of the field
and what the special `_` placeholder means.

This was mentioned as insufficiently explained in #582.

bors r+

Co-authored-by: Jonas Bushart <jonas@bushart.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants