2

我有一个特征可以管理到不同特征对象的转换。特征看起来像这样:(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)
    }
}

但是,如果我也想自动实现FooGee,因为我无法实现Foo两次,因此它不能以这种方式工作,据我所知,Rust 不支持这种方式。

// 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但我找不到对它们的任何深入解释,所以我现在有点卡住了。

4

1 回答 1

2

这个问题可以通过引入额外的间接级别来解决:

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_booand as_geeeach 移动到自己的特征,我们避免了重叠的实现。来自超特征的方法在特征对象中可用,因此Foo不必重新声明as_booand as_gee


虽然这在几乎总是实施的情况下效果很好,Boo如果不是这种情况,它仍然需要手动实施。考虑到 as_gee 应该在我的程序中大约 80% 的调用中返回这一事实,这是相当不幸的。GeeNone

我们可以通过使用专门化来解决这个问题(从 Rust 1.19 开始,它还不稳定,所以你需要使用 nightly 编译器)。我们需要将as_booand的实现as_gee从 trait 定义移动到impl适用于所有类型的 an,以便所有类型都实现FooAsBooand 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
    }
}
于 2017-08-06T20:11:16.720 回答