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

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

但如果我也想为 Gee 自动实现 Foo ,那么由于我无法实现 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也许可以实现这一点但我找不到任何深入的解释,所以我现在有点陷入困境。

最佳答案

这个问题可以通过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 开始还不稳定,所以你需要使用 nightly 编译器)。我们需要将 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
    }
}

关于generics - 是否可以自动实现将一个特征对象转换为另一个特征对象的特征?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45501780/

相关文章:

multithreading - 在 Rust 中,线程中使用的所有内容都必须是 `Send` 吗?

string - 拆分字符串并返回片段

rust - 有没有办法实现具有不同签名的特征方法?

因特征滥用而使用rust ?

Java 泛型代码使用 javac 编译,使用 Eclipse Helios 失败

c# - 为什么将 List<T>.AddRange 方法设为通用方法会影响性能?

带有 Void 的 Swift 泛型类型无法调用没有参数的方法

ios - 自定义运算符以简化 If-Let

rust - 如何配置 actix-web 以接受来自任何来源的 CORS 请求?

d - 判断模板参数是否为结构体