2017-09-06 55 views
2

我是Rust的新手,並且看到一些使用Box的人可以將許多實現特定特質的類型推送到Vec上。在使用泛型特性時,我遇到了一個問題。使用特質作爲Vec類型

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. 

這裏是我的代碼

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

我試圖用collidables作爲異質遊戲的對象,這將使我ncollide兼容形狀回送入碰撞檢測引擎的列表。編輯: 清除一些混淆。我不想構造並返回特質的實例。我只是想創建一個Vec,讓任何Collidable特性的實例都被推到它上面。

+0

的可能的複製[爲什麼一個特點不是構建自己?(https://stackoverflow.com/questions/38159771/why-can-a-trait-not-construct-itself)可能有更好的重複,但類似。 – loganfsmyth

+0

@loganfsmyth這不是我想要做的。我已經通過大部分的類似於此的例子閱讀並已經得到了他們通過使用VEC >工作。但是當我使用的是有一個通用型像VEC >>性狀,突然我得到這個錯誤。 –

+0

你期待'get_ncollide_shape'做什麼?這個錯誤是因爲'箱<可碰撞>'意味着,基本上你已經刪除有關除了它實現了特質的對象的所有數據。在這種情況下,'FN get_ncollide_shape >(個體經營) - >箱;'沒有任何意義,因爲就沒有辦法來調用它。有該功能的版本無限多的,因爲它是'T'參數,所以沒有辦法使用該功能,因爲它需要在運行時決定調用哪個版本,並且選項是已知的。 – loganfsmyth

回答

3

Rust是一種編譯語言,因此編譯代碼時需要知道生成機器代碼可能需要的所有信息。

當你說

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

struct Foo { 
    field: Box<MyTrait> 
} 

你告訴鏽病是Foo將包含含box任何實施MyTrait。通過對類型進行裝箱,編譯器將清除任何有關特性未涉及的數據類型的額外數據。這些trait objects被實現爲一組數據字段和功能表(稱爲vtable),該功能表包含由特徵暴露的功能,因此可以調用它們。

當您更改

fn do_thing() -> Box<u32>; 

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

它可能看起來很相似,但行爲有很大不同。讓我們正常功能例如

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

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

編譯器會執行一個叫做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); 
} 

實現關鍵的一點是,你要求它爲你的特質做同樣的事情。問題是編譯器無法做到。上面的例子依賴於事先知道do_thing在一種情況下用一個數字調用,在另一種情況下用一個布爾值調用,它可以100%確定地知道這些是使用函數的唯一兩種方式。

與您的代碼

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

編譯器不知道是什麼類型的do_thing將被調用,所以它沒有辦法產生你需要調用函數。要做到這一點,無論您將實現Collidable的結構轉換爲裝箱對象,都必須知道每種可能的返回類型get_ncollide_shape都可能具有,並且不受支持。

這個其他鏈接: