Как реализовать признак для параметризованного признака

У меня проблема с дизайном, когда я использую что-то вроде:

trait MyTrait<K: OtherTrait> { ... }

impl<K: OtherTrait, M: MyTrait<K>> AnyTrait for M { ... }

Я не могу реализовать трейт для этого трейта из-за ошибки E207 («параметр типа K не ограничен трейтом impl, self-типом или предикатами»).

Не находя способа избавиться от этой ошибки, я применяю этот не очень красивый обходной путь (подробный и структурированный без внутренней стоимости):

use std::fmt;
use std::marker::PhantomData;

pub trait MyTrait<K: fmt::Display> {
    fn get_some_k(&self) -> Option<K>;
}

/* // This is my target impl but results in E207 due to K not constrained
impl<K: fmt::Display, S: MyTrait<K>> fmt::Display for S {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.get_some_k().unwrap())
    }
} */
pub struct Ugly<'a, K: fmt::Display, S: 'a + MyTrait<K>>(&'a S, PhantomData<K>);
impl<'a, K: fmt::Display, S: MyTrait<K>> fmt::Display for Ugly<'a, K, S> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0.get_some_k().unwrap())
    }
}

fn main() { }

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

Я не нашел хорошего примера в std (например, нет реализации Display в чертах со связанным типом, например Iterator)?


person cheme    schedule 25.05.2015    source источник
comment
Просто подумал, что упомянул, что сталкиваюсь с той же проблемой, однако у меня нет возможности обернуть одеяло в Ugly, поскольку MyTrait является общедоступной чертой, доступной для пользователей, и требующей от них использования Ugly полностью разрушает API, добавляя ненужную сложность.   -  person mindTree    schedule 02.10.2015
comment
Возможный дубликат предоставления общих реализаций черт для настраиваемых черт   -  person wimh    schedule 26.11.2016


Ответы (1)


Вот реализация с использованием связанных типов (что означает, что вы можете реализовать MyTrait только для одного K каждого типа):

use std::fmt;

pub trait MyTrait {
    type K: fmt::Display;
    fn get_some_k(&self) -> Option<Self::K>;
}

impl<S: MyTrait> fmt::Display for S {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.get_some_k().unwrap())
    }
}

fn main() { }

Однако при таком разъяснении становится ясно, что этот подход тоже не будет работать, потому что вы реализуете Display для всех типов, реализующих MyTrait - типы, которые могут иметь свою собственную Display реализацию. Это запрещено, поэтому вы получаете E0210:

ошибка: параметр типа S должен использоваться как параметр типа для некоторого локального типа (например, MyStruct<T>); только признаки, определенные в текущем ящике, могут быть реализованы для параметра типа [E0210]

Обернуть его во что-то - как это сделал ваш Ugly - это единственный способ разрешить такую ​​реализацию. Или внедрите черту в свой ящик, а не в чужой (например, Display).

person Chris Morgan    schedule 25.05.2015
comment
спасибо, я подумал о связанном типе, но я нахожусь в том случае, когда мне нужно несколько реализаций для каждого типа, иначе он бы выполнил свою работу (мой вариант использования не Display). - person cheme; 25.05.2015