关联类型可通过特征和通用类型对可序列化数据进行规范化

时间:2019-03-13 14:21:17

标签: compiler-errors scope rust traits

我试图实现一种类型,该类型将对Tide中的响应“强制”某种模式,但会不断出现“特征只能使用项目...”编译器错误。

#![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"

编译器返回错误

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`

我不确定为什么会收到此错误,但是我觉得它与data: self.0.into_primary_data()行不够“具体”有关,并且不知道{{ 1}}和Self::Attrs是。但是,我知道,如果其中一个嵌套类型没有实现Self::Rels,但是从我的判断中,我也会遇到同样的错误(减去关于“来自特征的项目的帮助提示”)。 ,我已将这些界限添加到需要的任何地方。

我现在已经尝试过以大约一百万种方式来执行此操作,而且似乎还不太想出一种方法来为我的响应获取某种标准化的结构。

我正在使用serde::Serialize

1 个答案:

答案 0 :(得分:2)

您没有正确指定关联类型的完整范围。

Json仅在其包含的类型同时实现IntoResponseSend时才实现Serialize

impl<T: Send + Serialize> IntoResponse for Json<T>

您需要在关联类型的边界中包括Send

trait IntoPrimaryData: Send {
    type Attrs: serde::Serialize + Send;
    //                           ^^^^^^
    type Rels: serde::Serialize + Send;
    //                          ^^^^^^

    fn into_primary_data(self) -> PrimaryData<Self::Attrs, Self::Rels>;
}

调试步骤

此错误消息行似乎很有希望:

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`

这表明我们可以调用with_status,只是编译器不知道该类型是否实现了特征。从那里,我转到Json的文档中查看它是否实现了IntoRespose,如果实现了,则在什么条件下:

impl<T: Send + Serialize> IntoResponse for Json<T>

基于此,我们知道此T必须为PrimaryData<T::Attrs, T::Rels>,并且必须实现Send + Serialize

我们看到PrimaryData派生出Serialize

#[derive(Serialize)]
struct PrimaryData<Attrs, Rels> {

根据现有知识,我知道大多数derive d特性需要所有通用类型也都实现该特性。这种情况不太明显,但是对于Send来说也是如此。

从那里开始,要证明AttrsRels的特定类型实现SerializeSend。关联的类型范围只处理一个,而另一个不处理。

确定边界的位置取决于意图和样式-它们可以在函数,impl块或特征中进行。由于特征已经提到了Serialize,因此添加额外的界限似乎是很自然的地方。

我也犯了一个大错误-我假设您已经正确指定了边界并碰到了a compiler limitationalso)。只有当我尝试应用建议的重复项时,我才意识到界限是错误的。

另请参阅: