有没有一种方法可以将一种trait 转换成另一种?

我有trait FooBar,还有trait Vec<Box<dyn Foo>>.我知道Vec中的一些项目实现了Bartrait ,但我有没有办法针对它们?

我不明白这是否可能.

trait Foo {
    fn do_foo(&self);
}

trait Bar {
    fn do_bar(&self);
}

struct SomeFoo;

impl Foo for SomeFoo {
    fn do_foo(&self) {
        println!("doing foo");
    }
}

struct SomeFooBar;

impl Foo for SomeFooBar {
    fn do_foo(&self) {
        println!("doing foo");
    }
}

impl Bar for SomeFooBar {
    fn do_bar(&self) {
        println!("doing bar");
    }
}

fn main() {
    let foos: Vec<Box<dyn Foo>> = vec![Box::new(SomeFoo), Box::new(SomeFooBar)];

    for foo in foos {
        foo.do_foo();

        // if let Some(val) = foo.downcast_whatever::<Bar>() {
        //     val.bar();
        // }
    }
}

[Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8b637bddc4fc923ce705e84ad1d783d4)

推荐答案

不,没有办法在两个不相关的trait 之间进行转换.要理解为什么,我们必须理解trait 对象是如何实现的.首先,让我们看看TraitObject.

TraitObject反映了trait对象的实际实现方式.它们由两个指针组成:datavtable.data值只是对原始对象的引用:

#![feature(raw)]

use std::{mem, raw};

trait Foo {}
impl Foo for u8 {}

fn main() {
    let i = 42u8;
    let t = &i as &dyn Foo;
    let to: raw::TraitObject = unsafe { mem::transmute(t) };

    println!("{:p}", to.data);
    println!("{:p}", &i);
}

vtable点指向函数指针表.此表包含对每个实现的trait方法的引用,这些方法按编译器内部的某种方式排序.

对于这个假设输入

trait Foo {
    fn one(&self);
}

impl Foo for u8 {
    fn one(&self) { println!("u8!") }
}

这个表类似于这个伪代码

const FOO_U8_VTABLE: _ = [impl_of_foo_u8_one];

trait对象知道一个指向数据的指针和一个指向组成该trait的方法列表的指针.从这些信息中,有no way条信息可以获取任何其他数据.

不可能.正如您可能猜到的,您可以向vtable添加一个返回different trait对象的方法.在计算机科学中,所有问题都可以通过添加另一层间接寻址来解决(除了太多的间接寻址).

另见:

但是TraitObjectdata部分难道不能转化成 struct 吗

不安全.trait对象不包含关于原始类型的信息.它只有一个包含内存地址的原始指针.您可以将其转换为&Foo&u8&(),但编译器和运行时数据都不知道它最初是什么具体类型.

Any trait实际上是通过跟踪原始 struct 的类型ID来实现的.如果您要求引用正确的类型,trait将为您转换数据指针.

除了我用我的FooOrBar个trait描述的模式之外,还有没有其他模式可以处理这样的情况:我们需要迭代一系列trait对象,但对其中一些对象的处理略有不同?

  • 如果你拥有这些trait ,那么你可以在Bar个trait 上加as_foo,反之亦然.

  • 您可以创建一个包含Box<dyn Foo>Box<dyn Bar>的枚举,然后创建模式匹配.

  • 您可以将bar的主体移动到foo的主体中,以实现该实现.

  • 您可以实现第三个trait Quux,其中<FooStruct as Quux>::quux调用Foo::foo<BarStruct as Quux>::quux调用Bar::foo,然后是Bar::bar.

Rust相关问答推荐

为什么对不可复制数据的引用的取消引用没有O权限来避免Rust中的双重释放?

如何从使用mockall模拟的方法中返回self?

包含嵌套 struct 的CSV

如何最好地并行化修改同一Rust向量的多个切片的代码?

如何导出 rust 色二进制文件中的符号

如何获取Serde struct 的默认实例

如何在Rust中将选项<;选项<;字符串>;转换为选项<;选项&;str>;?

我可以在不收集或克隆的情况下,将一个带有Item=(key,val)的迭代器拆分成单独的key iter和val iter吗?

如何在函数中返回自定义字符串引用?

无符号整数的Rust带符号差

如何在AVX2中对齐/旋转256位向量?

不同类型泛型的映射

为什么BufReader实际上没有缓冲短寻道?

Const 上下文:从 init 函数创建具有 const 通用长度的数组

当推送到 HashMap 中的 Vector 时,类型 `()` 无法取消引用

错误:将自定义 proc_macro 与用Rust 的宝贝编写的属性一起使用时,无法在此范围内找到属性

是否可以预测堆栈溢出?

在使用大型表达式时(8k 行需要一小时编译),是否可以避免 Rust 中的二次编译时间?

`if let` 只是另一种编写其他 `if` 语句的方式吗?

如果我立即等待,为什么 `tokio::spawn` 需要一个 `'static` 生命周期?