是否可以自动实现将特征对象转换为另一个特征对象的特征?

Is it possible to auto implement a trait which casts a trait object to another trait object?

我有一个特征可以管理到不同特征对象的转换。 特征看起来像这样:(BooGee 都是不同的特征)

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)
    }
}

但是如果我也想为 Gee 自动实现 Foo 它不能这样工作,因为我无法实现 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 来实现这一点,但我找不到对它们的任何深入解释,所以我现在有点卡住了。

这个问题可以通过introducing an extra level of indirection解决:

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_booas_gee 移动到各自的特征,我们避免了重叠的实现。来自 supertraits 的方法在特征对象中可用,因此 Foo 不必重新声明 as_booas_gee.


While this works great in cases where Boo and Gee are close to always implemented, it still requires manual implementation when this is not the case. Considering the fact that as_gee should return None in about 80% of all calls in my program this is rather unfortunate.

我们可以使用 specialization 解决这个问题(从 Rust 1.19 开始不稳定,因此您需要使用夜间编译器)。我们需要将 as_booas_gee 的实现从特征定义移动到适用于所有类型的 impl,以便所有类型都实现 FooAsBooFooAsGee.

#![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
    }
}