如何将&Vec <Box <dyn Child >>转换为&Vec <Box <dyn Base >>,其中特征Child:Base {}?

时间:2020-08-04 02:20:06

标签: rust type-conversion

我已经尝试使用AsBase特性来this post,但不能完全忽略一个最小的例子。而且由于该帖子有些陈旧,因此缺少dyn有时会造成一些混乱。

这就是我想做的:

trait Entity {}
trait Part: Entity {}

trait System {
    fn parts(&self) -> &Vec<Box<dyn Entity>>; // This is the specification
}

struct System32 {
    parts: Vec<Box<dyn Part>>, // But this is what I have
}

impl System for System32 {
    fn parts(&self) -> &Vec<Box<dyn Entity>> {
        &self.parts // error: expected trait Entity, found trait Part

        // I've also tried:
        // &self.parts as &Vec<Box<dyn Entity>>
        // error: an `as` expression can only be used to convert between
        // primitive types or to coerce to a specific trait object
    }
}

这甚至可能吗?如果是,那我该如何进行类型转换?

2 个答案:

答案 0 :(得分:4)

这有可能吗?

否。

它有两个问题:首先,Rust不是基于继承的语言,trait B: A意味着“ B需要A”比“ B扩展A”更多。

现在,尽管可能不应该这样考虑Rust的特征,但是无论如何,都可以执行此“向上转换”,并且该语言最终可能包括此功能(部分用于多特征对象)。

但是这里有一个更大的问题:您要返回&Vec

这意味着矢量必须由 something 拥有,因为您只返回对其的引用。但是Box<dyn Entity>Box<dyn Part> 是完全不同的值

回到Rust不是基于继承的情况,B 的vtable不会嵌入A 的vtable,您不能只说“现在指向A的指针,因为它绝对不是[0]。

这意味着从Box<dyn Part>Box<dyn Entity>是一个完整的值转换,而不是就地将该值解释为另一种类型。

这意味着一个新的Box和一个新的Vec,这意味着您不能仅仅返回对现有Vec的引用,声称它是您想要的类型,即Vec本身的>内容需要更改。

[0]与C ++不同,我相信,至少对于SI情况,对于MI,您必须让父母之一进行简单的重新解释,但其他父母必须抵消孩子的指针才能获得正确的vtable,即使他们重新嵌入,所以我想你会有同样的问题

答案 1 :(得分:0)

上面的Masklinn解释了为什么您无法做自己想做的事情。 我将在这里尝试提供一种方法,该方法将使用与您相似的方法来解决您的问题。不过,也许有更好的方法,我们能否看到更广阔的前景。

实现模式的一种方法是使系统管理的实体类型与System特性的关联类型。这使您可以编写以下内容:

trait Entity {
    fn do_something(&self) {}
}
trait Part: Entity {}

trait System {
    type Entity: Entity + ?Sized;
    // I renamed this function from "parts" to "entities" because it seems that's what you want it to return
    fn entities(&self) -> &Vec<Box<Self::Entity>>; // This is the specification
    fn do_something_with_entities(&self) {
        for entity in self.entities() {
            entity.do_something();
        }
    }
}

struct System32 {
    parts: Vec<Box<dyn Part>>, // But this is what I have
}

impl System for System32 {
    type Entity = dyn Part;
    fn entities(&self) -> &Vec<Box<Self::Entity>> {
        &self.parts
    }
}

现在,如果您确实需要能够返回仅输出实际dyn Entity的某种形式的集合,因为例如您的系统管理着InnerEntity的不同类型...鉴于我不得不使用的肮脏技巧,我坚信这不是惯用的Rust,您应该表达出整个问题,以便人们可以提出更多建议惯用的解决方案比尝试在Rust中进行继承要好,但是无论如何,这种肮脏的编译最终会提供一个合理的接口,分配为零,这很有趣。

// ------ TRAIT SYSTEM
// From https://users.rust-lang.org/t/casting-traitobject-to-super-trait/33524/16
// This intermediate trait allows to work around Sized/?Sized requirements
pub trait IntoSuper<Super: ?Sized> {
    fn as_super(&self) -> &Super;
    fn as_super_mut(&mut self) -> &mut Super;
    fn into_super(self: Box<Self>) -> Box<Super>;
}

trait Entity {}
impl<'a, T: 'a + Entity> IntoSuper<dyn Entity + 'a> for T {
    fn as_super(&self) -> &(dyn Entity + 'a) {
        self
    }
    fn as_super_mut(&mut self) -> &mut (dyn Entity + 'a) {
        self
    }
    fn into_super(self: Box<Self>) -> Box<dyn Entity + 'a> {
        self
    }
}

trait Part: Entity + IntoSuper<dyn Entity> {}

// The 'r lifetime needs to be at the trait level because GATs are not stable yet
// https://github.com/rust-lang/rust/issues/44265
// This workaround somewhat simulates GATs
trait System: for<'r> SystemInner<'r> {}
impl<T: for<'r> SystemInner<'r>> System for T {}
trait SystemInner<'r> {
    /// Clone should be inexpensive
    type EntitiesIter: Iterator<Item = &'r dyn Entity> + ExactSizeIterator + Clone + 'r;
    fn entities(&'r self) -> Self::EntitiesIter; // This is the specification
}
type EntitiesIter<'r, Source> =
    std::iter::Map<std::slice::Iter<'r, Box<Source>>, fn(&'r Box<Source>) -> &'r dyn Entity>;
// ------ END OF TRAIT SYSTEM

struct System32 {
    parts: Vec<Box<dyn Part>>, // And this is what you have stored
}

impl<'r> SystemInner<'r> for System32 {
    type EntitiesIter = EntitiesIter<'r, dyn Part>;
    fn entities(&'r self) -> Self::EntitiesIter {
        self.parts.iter().map(|p| p.as_super())
    }
}
// System32 implements System because it implements SystemInner<'r> for every 'r

struct SomePart;
impl Entity for SomePart {}
impl Part for SomePart {}
fn main() {
    let system = System32 {
        parts: vec![Box::new(SomePart), Box::new(SomePart)],
    };

    let second_part: &dyn Entity = system.entities().nth(1).expect("We put two parts in our vec");
    for part_as_dyn_entity in system.entities() {
        // do stuff
    }
}