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



我已经尝试过使用AsBase特性来撰写这篇文章,但无法找到一个最小的例子。由于这篇文章有点过时,缺少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
}
}

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

这可能吗?

否。

它有两个问题:首先,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,即使它们是嵌入的,所以我认为你会有同样的问题

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
}
}

最新更新