Связанные типы для нормализации сериализуемых данных через признаки и универсальный тип - PullRequest
2 голосов
/ 13 марта 2019

Я пытался реализовать тип, который "принудительно" применял бы некоторую схему к моим ответам в 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() небыть достаточно "конкретным" и что неизвестно, что такое типы Self::Attrs и Self::Rels.Тем не менее, я знаю, что я также получаю ту же ошибку (за исключением подсказки о том, что «элементы из черт могут быть только ...»), если один из вложенных типов не реализует serde::Serialize, но из того, что я могу сказать, яЯ добавил эти границы везде, где они должны быть.

Я пытался сделать это так, как кажется, миллионами способов, и, похоже, не могу придумать способ получить некоторую нормализованную структуру для моегоответы.

Я использую rustc 1.34.0-nightly (02c4c2892 2019-02-26)

1 Ответ

2 голосов
/ 14 марта 2019

Вы не правильно указали полные границы для связанных типов.

Json реализует только IntoResponse, когда содержащийся в нем тип реализует как Send, так и 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.

Оттуда нужно доказать, что конкретные типы для Attrs и Rels реализуют Serializeи Send.Связанные границы типов обрабатывают одну, но не другую.

Решение о том, где разместить границы, зависит от намерения и стиля - они могут идти по функции, блоку impl или по признаку.Поскольку в признаке уже упоминалось Serialize, казалось, что это естественное место для добавления дополнительной границы.

Я также сделал одну большую ошибку - я предположил, что вы правильно указали границы и столкнулись с ограничение компилятора ( также ).Только когда я попытался применить предложенный дубликат, я понял, что границы были неправильными.

См. Также:

...