Можно ли автоматически реализовать черту, которая преобразует объект черты в другой объект черты?
У меня есть черта, которая управляет преобразованием в различные объекты черты. Черта выглядит так: (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
}
}