Как написать трейт, привязанный к ссылке на ассоциированный тип самого трейта?

У меня есть этот код:

extern crate serde;

use serde::de::DeserializeOwned;
use serde::Serialize;

trait Bar<'a, T: 'a>
where
    T: Serialize,
    &'a T: DeserializeOwned,
{
}

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

trait Bar {
    type T: Serialize;
}

Я не могу понять, как указать другую границу.

В конечном счете, я хочу использовать такую ​​функцию:

extern crate serde_json;

fn test<I: Bar>(t: I::T) -> String {
    serde_json::to_string(&t).unwrap()
}

person corvus_192    schedule 29.04.2018    source источник


Ответы (1)


«Правильное» решение состоит в том, чтобы поместить границы черты, но со ссылкой на связанный с ней тип. В этом случае вы также можете использовать границы с более высоким рейтингом для обработки ссылки:

trait Bar
where
    Self::T: Serialize,
//  ^^^^^^^ Bounds on an associated type  
    for<'a> &'a Self::T: DeserializeOwned,
//  ^^^^^^^^^^^ Higher-ranked trait bounds       
{
    type T;
}

Однако это пока не работает.

Я считаю, что вам нужно:

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

fn test<I: Bar>(t: I::T) -> String
where
    for<'a> &'a I::T: DeserializeOwned,
{
    serde_json::to_string(&t).unwrap()
}
person Shepmaster    schedule 29.04.2018
comment
Спасибо, я не знал, что вы можете Self в таком случае. Но, похоже, это не работает, как я ожидал. У меня есть эта функция: fn test<I: Bar>(t: I::T) -> String { serde_json::to_string(&t).unwrap() }. Компиляция завершается ошибкой, трейт for<'a> DeserializeOwned не реализован для &'a <I as Bar>::T. Я думаю, что, требуя, чтобы t был I::T, должен быть DeserializeOwned для &'at forall 'a. Я что-то неправильно понимаю? - person corvus_192; 29.04.2018
comment
@corvus_192 Я думаю, это потому, что в подписи test нет I, чтобы компилятор мог его увидеть. Вы могли бы реализовать несколько типов I, для которых <I as Bar>::T было бы одним и тем же, верно? - person trentcl; 29.04.2018
comment
@trentcl В моем случае есть только одна реализация Bar, но, очевидно, их может быть больше одной. Почему это имеет значение, если каждая реализация предоставляет тип T, удовлетворяющий заданным ограничениям? - person corvus_192; 30.04.2018
comment
@corvus_192 Компилятор не очень хорошо доказывает универсальные утверждения. Подпись, подобная fn test<I: Bar>(t: I::T) -> String, подразумевает, что вы можете выбрать любой I, реализующий Bar, и компилятор должен доказать, что для каждого I существует реализация DeserializeOwned, где <I as Bar>::T любого типа. t на самом деле есть. У меня плохо получается объяснять. Дело в том, что это логически правильно, но компилятор не может доказать, что не существует некоторого I, для которого &<I as Bar>::T не реализует DeserializeOwned. - person trentcl; 30.04.2018
comment
Результатом всего этого является то, что вы должны дублировать привязку for<'a> &'a I::T: DeserializeOwned на test, потому что просто иметь ее на Bar недостаточно. пример - person trentcl; 30.04.2018