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

时间:2017-08-04 08:25:37

标签: generics rust traits boilerplate trait-objects

我有一个特性来管理对不同特征对象的转换。 特征如下所示:(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)
    }
}

但是,如果我还想为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 个答案:

答案 0 :(得分:2)

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各自移动到自己的特征,我们避免了重叠的实现。特质对象中提供了超级特征的方法,因此Foo无需重新声明as_booas_gee

  

虽然这在BooGee 关闭始终实施的情况下效果很好,但在不是这种情况时仍需要手动实施。考虑到as_gee应该在我的程序中大约80%的所有调用中返回None,这是非常不幸的。

我们可以使用specialization解决这个问题(从Rust 1.19起不稳定,所以你需要使用夜间编译器)。我们需要将as_booas_gee的实现从特征定义移动到适用于所有类型的impl,以便所有类型实现FooAsBoo和{{1} }。

FooAsGee