I tried to implement a type that would "enforce" some schema to my responses in Tide but keep getting the "Items from traits can only be used..." compiler error.
#![feature(async_await, futures_api, await_macro, arbitrary_self_types)]
#![allow(proc_macro_derive_resolution_fallback)]
use serde_derive::Serialize;
use tide::{body::Json, IntoResponse, Response};
#[derive(Serialize)]
struct Document<Attrs, Rels> {
data: PrimaryData<Attrs, Rels>,
}
#[derive(Serialize)]
struct PrimaryData<Attrs, Rels> {
id: i32,
kind: String,
attributes: Attrs,
relationships: Rels,
}
trait IntoPrimaryData: Send {
type Attrs: serde::Serialize;
type Rels: serde::Serialize;
fn into_primary_data(self) -> PrimaryData<Self::Attrs, Self::Rels>;
}
struct ServiceResponse<T: IntoPrimaryData>(T);
impl<T: IntoPrimaryData> IntoResponse for ServiceResponse<T> {
fn into_response(self) -> Response {
Json(Document {
data: self.0.into_primary_data(),
})
.with_status(http::status::StatusCode::OK)
.into_response()
}
}
#[derive(Serialize)]
struct User {
id: i32,
primary_email: String,
}
#[derive(Serialize)]
struct UserAttrs {
primary_email: String,
}
impl IntoPrimaryData for User {
type Attrs = UserAttrs;
type Rels = ();
fn into_primary_data(self) -> PrimaryData<Self::Attrs, Self::Rels> {
PrimaryData {
id: self.id,
kind: "user".into(),
attributes: UserAttrs {
primary_email: self.primary_email,
},
relationships: (),
}
}
}
fn main() {}
[dependencies]
tide = "0.0.5"
http = "0.1.16"
serde = "1.0.89"
serde_derive = "1.0.89"
The compiler returns the error
error[E0599]: no method named `with_status` found for type `tide::body::Json<Document<<T as IntoPrimaryData>::Attrs, <T as IntoPrimaryData>::Rels>>` in the current scope
--> src/main.rs:34:10
|
34 | .with_status(http::status::StatusCode::OK)
| ^^^^^^^^^^^
|
= note: the method `with_status` exists but the following trait bounds were not satisfied:
`tide::body::Json<Document<<T as IntoPrimaryData>::Attrs, <T as IntoPrimaryData>::Rels>> : tide::response::IntoResponse`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `with_status`, perhaps you need to implement it:
candidate #1: `tide::response::IntoResponse`
I'm not sure why I'm getting this error but I feel like it has something to do with the line data: self.0.into_primary_data()
not being "specific" enough and that it is not known what the types of Self::Attrs
and Self::Rels
are. However, I know that I also get this same error (minus the help tip about "items from traits can only be...") if one of the nested types don't implement serde::Serialize
but from what I can tell, I've added those bounds everywhere they need to be.
I've tried doing this in what feels like a million ways now and can't quite seem to come up with a way to get some normalized structure for my responses.
I'm using rustc 1.34.0-nightly (02c4c2892 2019-02-26)
You haven't correctly specified the complete bounds on your associated types.
Json
only implements IntoResponse
when the type it contains implements both Send
and Serialize
:
impl<T: Send + Serialize> IntoResponse for Json<T>
You need to include Send
in the bounds for the associated types:
trait IntoPrimaryData: Send {
type Attrs: serde::Serialize + Send;
// ^^^^^^
type Rels: serde::Serialize + Send;
// ^^^^^^
fn into_primary_data(self) -> PrimaryData<Self::Attrs, Self::Rels>;
}
This line of the error message seemed promising:
the method `with_status` exists but the following trait bounds were not satisfied:
`tide::body::Json<Document<<T as IntoPrimaryData>::Attrs, <T as IntoPrimaryData>::Rels>> : tide::response::IntoResponse`
This states that we could call with_status
except that the compiler didn't know that the type implemented the trait. From there, I went to the documentation of Json
to see if it implemented IntoRespose
and if so, under what conditions:
impl<T: Send + Serialize> IntoResponse for Json<T>
Based on that, we know that this T
must be PrimaryData<T::Attrs, T::Rels>
and it must implement Send + Serialize
.
We see that PrimaryData
derives Serialize
:
#[derive(Serialize)]
struct PrimaryData<Attrs, Rels> {
By existing knowledge, I know that most derive
d traits require that all of the generic types also implement the trait. It's less obvious, but the same is true for Send
.
From there, it's a matter of proving that the specific types for Attrs
and Rels
implement Serialize
and Send
. The associated type bounds handled one but not the other.
Deciding where to place the bounds is a matter of intent and style — they could go on the function, the impl
block, or in the trait. Since the trait already had mention of Serialize
, it seemed a natural place to add the additional bound.
I also made one large misstep — I assumed that you had correctly specified the bounds and were running into a compiler limitation ( also ). Only when I tried to apply the suggested duplicate did I realize that the bounds were incorrect.
See also:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.