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?
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>
{
/* ... */
}
}
@dtolnay Okay! But, I am just being curious now, is there a way to do that with rust syntax at all?
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.
Most helpful comment
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.htmlExample [play]:
There is no such behavior currently expressible for regular
#[derive]
s, so the bounds forHashSet
'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.