我已经尝试使用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
}
}
这甚至可能吗?如果是,那我该如何进行类型转换?
答案 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
}
}