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

How to write a generic type bounds for deserialize trait? #964

Closed
iddm opened this issue Jun 22, 2017 · 3 comments
Closed

How to write a generic type bounds for deserialize trait? #964

iddm opened this issue Jun 22, 2017 · 3 comments
Labels

Comments

@iddm
Copy link

iddm commented Jun 22, 2017

I want to declare a generic struct with de-serialization but I don't know how to correctly require a Deserialize trait to be implemented for a generic type.

#[derive(Debug, Clone, Deserialize)]
pub struct Response<ResponseResult: Deserialize> {
    /// some fields ...
    pub result: ResponseResult,
}

Gives the error:


error[E0106]: missing lifetime specifier
  --> src/model.rs:43:37
   |
43 | pub struct Response<ResponseResult: Deserialize> {
   |                                     ^^^^^^^^^^^^^^ expected lifetime parameter

I understand that Deserialize trait requires a lifetime but I don't know how to specialize it right here. Is there a way?

@dtolnay
Copy link
Member

dtolnay commented Jun 22, 2017

In general, you should not write trait bounds on structs. See https://github.com/Manishearth/rust-clippy/issues/1689.

The solution here is not writing a trait bound. You haven't written one for Debug or Clone, so don't write one for Deserialize either.

#[derive(Debug, Clone, Deserialize)]
pub struct Response<ResponseResult> {
    /// some fields ...
    pub result: ResponseResult,
}

This will expand to something like:

impl<'de, ResponseResult> Deserialize<'de> for Response<ResponseResult>
    where ResponseResult: Deserialize<'de>
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: Deserializer<'de>
    {
        /* ... */
    }
}

@iddm
Copy link
Author

iddm commented Jun 28, 2017

@dtolnay Okay! But, I am just being curious now, is there a way to do that with rust syntax at all?

@iddm iddm closed this as completed Jun 28, 2017
@CAD97
Copy link

CAD97 commented Feb 9, 2018

If someone else ends up here like I did, where contained types have more complicated bounds for their De/Serialize implementation, the answer is given here: https://serde.rs/attr-bound.html

Example [play]:

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

use serde::ser::Serialize;
use serde::de::Deserialize;

use std::collections::HashSet;
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hash};

#[derive(Serialize, Deserialize)]
pub struct MySet<T, S = RandomState>(
    #[serde(bound(serialize = "HashSet<T, S>: Serialize",
                  deserialize = "HashSet<T, S>: Deserialize<'de>"))]
    HashSet<T, S>,
);

#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Serialize, Deserialize)]
pub struct MySet_<T: Eq + Hash, S: BuildHasher = RandomState>(
    #[serde(bound(deserialize = "HashSet<T, S>: Deserialize<'de>"))]
    HashSet<T, S>,
);

There is no such behavior currently expressible for regular #[derive]s, so the bounds for HashSet's basic trait impls have to be added to the struct's. I would argue that this is good design as these bounds are required for the type to be of any use, but others disagree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

3 participants