简体   繁体   English

使用特征作为Vec类型

[英]Use Trait as Vec Type

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. 我是Rust的新手,看到了一些使用Box来允许将实现特定特征的许多类型推送到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. 我正在尝试使用可碰撞对象作为异类游戏对象的列表,这些对象将使我可以与ncollide兼容的Shapes反馈给碰撞检测引擎。

EDIT: To clear up some confusion. 编辑:清除一些混乱。 I'm not trying to construct and return an instance of a Trait. 我不是要构造并返回Trait的实例。 I'm just trying to create a Vec that will allow any instance of the Collidable trait to be pushed onto it. 我只是在尝试创建一个Vec,以允许将Collidable特性的任何实例推入该Vec。

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. Rust是一种编译语言,因此在编译代码时,它需要知道生成机器代码可能需要的所有信息。

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 . 您告诉Rust, Foo将包含一个包含任何实现MyTraitbox 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. 这些特征对象被实现为一组数据字段和一个函数表(称为vtable ),其中包含该特征公开的函数,因此可以对其进行调用。

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 编译器执行所谓的monomorphization ,这意味着编译器中的代码从本质上变为

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. 上面的示例依赖于提前知道在一种情况下用数字调用do_thing而在另一种情况下用布尔值调用do_thing情况,并且可以100%地确定这是使用函数的两种方式。

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. 编译器不知道将使用哪种类型的do_thing进行调用,因此它无法生成需要调用的函数。 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. 为此,无论将实现Collidable的结构转换为盒装对象的任何地方,都必须知道get_ncollide_shape可能具有的每种可能的返回类型,而这不受支持。

Other links for this: 其他链接:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM