0

I've tried this post using the AsBase trait but couldn't quite get to a minimal example. And since that post is a bit old, the lack of dyn sometimes gets a bit confusing.

This is what I thought I could do:

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

Is this even possible? If yes, then how can I do the type conversion?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
LeoVen
  • 632
  • 8
  • 18

2 Answers2

4

Is this even possible?

No.

There are two issues with it: first, Rust is not an inheritance-based language, trait B: A means "B requires A" more than "B extends A".

Now although this is probably not how Rust's trait should be thought of, there are ways to perform this "upcasting" regardless, and the language may eventually include this feature (in part for multi-trait objects).

However there is a bigger issue here: you're returning an &Vec.

This means that vector has to be owned by something as you're only returning a reference to it. But a Box<dyn Entity> and a Box<dyn Part> are completely different values.

Going back to Rust not being inheritance-based, the vtable for B does not embed the vtable for A, you can't just say "this is now a pointer to an A" because it absolutely is not[0].

This means going from Box<dyn Part> to Box<dyn Entity> is a full value conversion, not an in-place reinterpretation of the value as a different type.

Which means a new Box, and a new Vec, which means you can't just return a reference to an existing Vec claiming it's the type you want, the contents of the Vec itself need to change.

[0] unlike C++ I believe, at least for SI cases, with MI you'd have one of the parents be a simple reinterpretation but the others would have to offset the child pointer to get the correct vtable even if they're embedded, so I think you'd have the same issue

Masklinn
  • 34,759
  • 3
  • 38
  • 57
0

Masklinn above explained why you cannot do what you wanted to do. I'm going here to try to provide an approach that would work for solving your problem using an approach similar to yours. Maybe there are better approaches though, could we see the bigger picture.

One way to achieve your pattern would be to have the type of entity your system manages be an associated type of your System trait. This enables you to write the following:

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

Now if you reaally need to be able to return some form of collection that outputs only actual dyn Entity because e.g. your system manages different types of what would be InnerEntity... well given the amount of dirty tricks I had to use I strongly beleive that is not idiomatic Rust, and you should probably express the whole problem so people can suggest more idiomatic solutions than trying to do inheritance in Rust, but in any case, this compilation of dirty stuff that eventually provides a reasonable interface with zero allocations was pretty fun to write.

// ------ 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
    }
}
Ten
  • 1,283
  • 9
  • 12