【问题标题】:Encompassing Trait Objects within a Base Trait Object在基本特征对象中包含特征对象
【发布时间】:2015-07-23 15:52:07
【问题描述】:

对不起,如果这看起来微不足道,但我正在尝试做一个简单的操作,但我很难做到。我只想拥有两个特征对象,其中一个具有一个包含一堆其他对象的 Vector。

trait MetaClass {
    fn new() -> Self;
    fn add(&self, subtrait: Box<SubClass>);
}

struct MetaStruct {
    elems: Vec<Box<SubClass>>,
}

impl MetaClass for MetaStruct{
    fn new() -> MetaStruct {
        MetaStruct{
            elems: Vec::new(),
        }
    }

    fn add(&self, subtrait: Box<SubClass>){
        // if I reformulate the above to 'fn add(&self, subtrait: SubClass){'
        // and use the below I get the trait `core::marker::Sized` is not implemented for the type `SubClass`
        //self.elems.push(Box::new(subtrait));
        self.elems.push(subtrait);
    }
}


trait SubClass{
    fn new() -> Self;
}

struct MySubClass {
    data: i32,
}

impl SubClass for MySubClass {
    fn new() -> MySubClass{
        MySubClass{
            data: 10,
        }
    }
}


fn main(){
    let mut meta = Box::new(MetaStruct::new());
    // ideally I just want to do meta.add(MySubClass::new()) but as mentioned above that has some sizing issues :'(
    meta.add(Box::new(MySubClass::new()));
}

我得到的错误是:

<anon>:45:11: 45:38 error: cannot convert to a trait object because trait `SubClass` is not object-safe [E0038]
<anon>:45   meta.add(Box::new(MySubClass::new()));
                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~

这里是 rust play 的链接:http://is.gd/pjLheJ

我也尝试了以下方法,但得到了同样的错误:

meta.add(Box::new(MySubClass::new()) as Box<SubClass>);

理想情况下,如果有一种方法可以使用 Rust 的静态调度来做到这一点,那将是理想的,但我也可以使用动态调度。在每种情况下,我认为让 MetaClass 实际上拥有子类的对象是有意义的,所以我不想传递对它的引用,而是传递整个对象本身。

【问题讨论】:

标签: rust


【解决方案1】:

我只想拥有两个 trait 对象,其中一个有一个 Vector,其中包含一堆另一个对象。

这很简单:

trait TraitOne {
    fn add(&mut self, another: Box<TraitTwo>);
}

trait TraitTwo {
    fn value(&self) -> u8; 
}

struct Container(Vec<Box<TraitTwo>>);
impl TraitOne for Container {
    fn add(&mut self, another: Box<TraitTwo>) {
        self.0.push(another);
    }
}

struct ThingOne(u8);
impl TraitTwo for ThingOne {
    fn value(&self) -> u8 { self.0 }
}

struct ThingTwo(u8, u8);
impl TraitTwo for ThingTwo {
    fn value(&self) -> u8 { self.0 + self.1 }
}

fn main() {
    let mut trait_1: Box<TraitOne> = Box::new(Container(vec![]));

    let thing_1: Box<TraitTwo> = Box::new(ThingOne(42));
    let thing_2: Box<TraitTwo> = Box::new(ThingTwo(21, 21));

    trait_1.add(thing_1);
    trait_1.add(thing_2);
}

你真正的错误是关于对象安全。 Huon Wilson(Rust 核心团队成员)写了一个great blog post about this

在你的情况下,你有这个特点:

trait SubClass{
    fn new() -> Self;
}

这不是对象安全的,引用 Huon(强调我的):

有两种基本方式可以发生这种情况,作为参数或作为返回值,在任何一种情况下,对 Self 类型的引用都意味着它必须匹配 self 值的类型,true 类型这在编译时是未知的

即需要在栈或堆上分配多少字节来保存fn new() -&gt; Self返回的对象?你无法知道,因为你只有一个 trait 对象。

一种解决方案是确保Self 在您的特征中是Sized,根据followup blog post

trait SubClass {
    fn new() -> Self where Self: Sized;
}

然后,只需修复 trait 中的一些可变性不匹配,它就会编译。

【讨论】:

  • 呸,没想到这么容易实现大小。这么简单的事情解决了我的问题!再次感谢您!
猜你喜欢
  • 2015-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-23
  • 2017-10-25
  • 1970-01-01
  • 2022-01-05
相关资源
最近更新 更多