Можно ли автоматически реализовать черту, которая преобразует объект черты в другой объект черты?

У меня есть черта, которая управляет преобразованием в различные объекты черты. Черта выглядит так: (Boo а также Gee это разные черты)

trait Foo {
    fn as_boo(&mut self) -> Option<&mut Boo> {
        None
    }

    fn as_gee(&mut self) -> Option<&mut Gee> {
        None
    }
}

Чтобы уменьшить количество стандартного кода, я хочу автоматически изменить эту реализацию на эту, если структура реализует Boo / Gee:

#[derive(Boo)]
struct W {}

impl Foo for W {
    fn as_boo(&mut self) -> Option<&mut Boo> {
        Some(self)
    }
}

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

impl<T: Boo> Foo for T {
    fn as_boo(&mut self) -> Option<&mut Boo> {
        Some(self)
    }
}

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

// This does not compile because Foo might get implemented twice.
impl<T: Boo> Foo for T {
    fn as_boo(&mut self) -> Option<&mut Boo> {
        Some(self)
    }
}

impl<T: Gee> Foo for T {
    fn as_gee(&mut self) -> Option<&mut Gee> {
        Some(self)
    }
}

Это может быть возможно, используя Procedural Macros но я не смог найти подробного объяснения их, поэтому я застрял сейчас.

1 ответ

Решение

Эта проблема может быть решена путем введения дополнительного уровня косвенности:

trait Boo {}
trait Gee {}

trait FooAsBoo {
    fn as_boo(&mut self) -> Option<&mut Boo> {
        None
    }
}

trait FooAsGee {
    fn as_gee(&mut self) -> Option<&mut Gee> {
        None
    }
}

trait Foo: FooAsBoo + FooAsGee {}

impl<T: Boo> FooAsBoo for T {
    fn as_boo(&mut self) -> Option<&mut Boo> {
        Some(self)
    }
}

impl<T: Gee> FooAsGee for T {
    fn as_gee(&mut self) -> Option<&mut Gee> {
        Some(self)
    }
}

impl<T: FooAsBoo + FooAsGee> Foo for T {} // if there's nothing else in Foo

struct W;
impl Boo for W {}
impl Gee for W {}

fn main() {
    let mut w = W;
    let foo = &mut w as &mut Foo;
    let boo = foo.as_boo();
}

Двигая as_boo а также as_gee каждому свой характер, мы избегаем перекрывающихся реализаций. Методы из суперпризнаков доступны в объектах признаков, поэтому Foo не нужно переопределять as_boo а также as_gee,


Хотя это прекрасно работает в тех случаях, когда Boo а также Gee всегда близки к тому, чтобы быть реализованными, это все еще требует ручной реализации, когда это не так. Учитывая тот факт, что as_gee должен вернуть None примерно в 80% всех звонков в моей программе это довольно неудачно.

Мы можем решить эту проблему, используя специализацию (которая не стабильна с Rust 1.19, поэтому вам нужно использовать ночной компилятор). Нам нужно перенести реализации as_boo а также as_gee от определения черты до impl это относится ко всем типам, так что все типы реализуют FooAsBoo а также FooAsGee,

#![feature(specialization)]

trait FooAsBoo {
    fn as_boo(&mut self) -> Option<&mut Boo>;
}

trait FooAsGee {
    fn as_gee(&mut self) -> Option<&mut Gee>;
}

impl<T> FooAsBoo for T {
    default fn as_boo(&mut self) -> Option<&mut Boo> {
        None
    }
}

impl<T> FooAsGee for T {
    default fn as_gee(&mut self) -> Option<&mut Gee> {
        None
    }
}
Другие вопросы по тегам