我可以在两个特征之间施展吗?

Chr*_*oph 3 traits rust

我发誓我搜索了所有互联网,我努力去理解我发现的所有相似的答案.但是,我仍然无法理解这是否可行.

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<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();
        }*/
    }
}
Run Code Online (Sandbox Code Playgroud)

围栏:http://is.gd/CPnSCu

我的问题基本上是否有一种方法可以从一个特征转变为另一个特征.

我有两个特质FooBarVec<Box<Foo>>.我知道vec中的一些项目实现了Bar特性,我想知道是否有任何方法可以针对它们?

更新

到目前为止,我发现的唯一解决方案是引入FooOrBar具有显式转换器方法的第三个特征,并为两种类型实现该特性.虽然它感觉不适合工作的正确工具;)

trait FooOrBar {
    fn to_bar(&self) -> Option<&Bar>;
    fn to_foo(&self) -> Option<&Foo>;
}

impl FooOrBar for SomeFooBar {
    fn to_bar(&self) -> Option<&Bar> {
        Some(self)
    }

    fn to_foo(&self) -> Option<&Foo> {
        None
    }
}

impl FooOrBar for SomeFoo {
    fn to_bar(&self) -> Option<&Bar> {
        None
    }

    fn to_foo(&self) -> Option<&Foo> {
        Some(self)
    }
}

fn main () {

    let foos:Vec<Box<FooOrBar>> = vec!(Box::new(SomeFoo), Box::new(SomeFooBar));

    for foo in foos {
        foo.to_foo().map(|foo| foo.do_foo());
        foo.to_bar().map(|foo| foo.do_bar());
    }
}
Run Code Online (Sandbox Code Playgroud)

She*_*ter 7

没有.没有办法在两个不相关的特征之间施展.要理解原因,我们必须了解如何实现特征对象.首先,让我们来看看TraitObject.

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

#![feature(raw)]

trait Foo {}
impl Foo for u8 {}

use std::{raw, mem};

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

    println!("{:p}", to.data);
    println!("{:p}", &i);
}
Run Code Online (Sandbox Code Playgroud)

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

// For this hypothetical input
trait Foo {
    fn one(&self);
}

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

// The table is something like this pseudocode
const FOO_U8_VTABLE = [impl_of_foo_u8_one];
Run Code Online (Sandbox Code Playgroud)

因此,特征对象知道指向数据的指针以及指向构成该特征的方法列表的指针.根据这些信息,无法获得任何其他数据.

好吧,几乎没办法.正如您可能猜到的,您可以向vtable添加一个返回不同特征对象的方法.在计算机科学中,所有问题都可以通过添加另一层间接(除了太多的间接层)来解决.

但是不能将这data部分TraitObject转化为结构

不安全,没有.特征对象不包含有关原始类型的信息.它只是一个包含内存中地址的原始指针.您可能会将其无法安全转换为a &Foo或a &u8或a &(),但编译器和运行时数据都不知道它最初的具体类型.

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

是否有一种模式,而不是我用我的FooOrBar特性描述的模式来处理这样的情况,我们需要迭代一堆特征对象,但是它们中的一些稍微不同?

  • 如果您拥有这些特征,那么您可以添加as_fooBar特征,反之亦然.

  • 您可以创建一个包含a Box<Foo>或a Box<Bar>和then模式匹配的枚举.

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

  • 您可以实现第三个特性Quux,其中呼叫<FooStruct as Quux>::quux呼叫Foo::foo<BarStruct as Quux>::quux呼叫Bar::foo后跟呼叫Bar::bar.

  • 我不认为 C++ 也使用 v 表并将运行时类型信息存储在常规虚拟方法列表之上,因此使用 v 表本身并不排除向下转换。只是 Rust 不存储此类信息。 (2认同)