Use Trait as Vec Type

喜你入骨 提交于 2021-01-27 19:04:44

问题


I'm new to Rust and have seen some examples of people using Box to allow pushing many types that implement a certain Trait onto a Vec. When using a Trait with Generics, I have run into an issue.

error[E0038]: the trait `collision::collision_detection::Collidable` cannot be made into an object
  --> src/collision/collision_detection.rs:19:5
   |
19 |     collidables: Vec<Box<Collidable<P, M>>>,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `collision::collision_detection::Collidable` cannot be made into an object
   |
   = note: method `get_ncollide_shape` has generic type parameters

error: aborting due to previous error

error: Could not compile `game_proto`.

To learn more, run the command again with --verbose.

Here is my code

extern crate ncollide;
extern crate nalgebra as na;

use self::ncollide::shape::Shape;
use self::ncollide::math::Point;
use self::ncollide::math::Isometry;
use self::na::Isometry2;

pub trait Collidable<P: Point, M> {
    fn get_ncollide_shape<T: Shape<P, M>>(&self) -> Box<T>;
    fn get_isometry(&self) -> Isometry2<f64>;
}

pub struct CollisionRegistry<P, M>
where
    P: Point,
    M: Isometry<P>,
{
    collidables: Vec<Box<Collidable<P, M>>>,
}

impl<P: Point, M: Isometry<P>> CollisionRegistry<P, M> {
    pub fn new() -> Self {
        let objs: Vec<Box<Collidable<P, M>>> = Vec::new();
        CollisionRegistry { collidables: objs }
    }

    pub fn register<D>(&mut self, obj: Box<D>)
    where
        D: Collidable<P, M>,
    {
        self.collidables.push(obj);
    }
}

I'm trying to use collidables as a list of heterogenous game objects that will give me ncollide compatible Shapes back to feed into the collision detection engine.

EDIT: To clear up some confusion. I'm not trying to construct and return an instance of a Trait. I'm just trying to create a Vec that will allow any instance of the Collidable trait to be pushed onto it.


回答1:


Rust is a compiled language, so when it compiles your code, it needs to know all of the information it might need to generate machine code.

When you say

trait MyTrait {
  fn do_thing() -> Box<u32>;
}

struct Foo {
   field: Box<MyTrait>
}

you are telling Rust that Foo will contain a box containing anything implementing MyTrait. By boxing the type, the compiler will erase any additional data about the data type that isn't covered by the trait. These trait objects are implemented as a set of data fields and a table of functions (called a vtable) that contains the functions exposed by the trait, so they can be called.

When you change

fn do_thing() -> Box<u32>;

to

fn do_thing<T>() -> Box<T>;

it may look similar, but the behavior is much different. Let's take a normal function example

fn do_thing<T>(val: T) { }

fn main() {
  do_thing(true);
  do_thing(45 as u32);
}

the compiler performs what is a called monomorphization, which means your code in the compiler becomes essentially

fn do_thing_bool(val: bool) { }
fn do_thing_num(val: u32) { }

fn main() {
  do_thing_bool(true);
  do_thing_num(45 as u32);
}

The key thing to realize is that you are asking it to do the same thing for your trait. The problem is that the compiler can't do it. The example above relies on knowing ahead of time that do_thing is called with a number in one case and a boolean in another, and it can know with 100% certainty that those are the only two ways the function is used.

With your code

trait MyTrait {
  fn do_thing<T>() -> Box<T>;
}

the compiler does not know what types do_thing will be called with, so it has no way to generate functions you'd need to call. To do that, wherever you convert the struct implementing Collidable into a boxed object it would have to know every possible return type get_ncollide_shape could have, and that is not supported.

Other links for this:

  • Understanding Traits and Object Safety
  • https://www.reddit.com/r/rust/comments/3an132/how_to_wrap_a_trait_object_that_has_generic/


来源:https://stackoverflow.com/questions/46065104/use-trait-as-vec-type

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!