2015-05-16 3 views
4
trait Actor{ 
    fn actor(&self); 
} 
trait Health{ 
    fn health(&self); 
} 
struct Plant; 
impl Actor for Plant{ 
    fn actor(&self){ 
     println!("Plant Actor"); 
    } 
} 
struct Monster{ 
    health: f32 
} 
impl Actor for Monster{ 
    fn actor(&self){ 
     println!("Monster Actor"); 
    } 
} 
impl Health for Monster{ 
    fn health(&self){ 
     println!("Health: {}",self.health); 
    } 
} 
fn main() { 
    let plant = Box::new(Plant); 
    let monster = Box::new(Monster{health: 100f32}); 

    let mut actors : Vec<Box<Actor>> = Vec::new(); 
    actors.push(plant); 
    actors.push(monster); 

    for a in &actors{ 
     a.actor(); 
     /* Would this be possible? 
     let health = a.get_trait_object::<Health>(); 
     match health{ 
      Some(h) => {h.health();}, 
      None => {println!("Has no Health trait");} 
     } 
     */ 
    } 
} 

Мне интересно, возможно ли что-то подобное?Можно ли проверить, реализует ли объект признак во время выполнения?

let health = a.get_trait_object::<Health>(); 
match health{ 
    Some(h) => {h.health();}, 
    None => {println!("Has no Health trait");} 
} 
+0

Существует в настоящее время множество РЛК к получению понижающее приведение (в той или иной форме) в Rust. Это будет возможно в один прекрасный день, но не сейчас. –

ответ

5

Начиная с версии 1.0, нет. Ржавчина не обеспечивает никакой поддержки динамического понижения, за исключением Any; тем не менее, это позволяет вам только опускать конкретный конкретный тип значения, а не произвольные черты, которые реализуются конкретным типом.

I верьте, вы можете реализовать такое литье вручную, но для этого потребуется небезопасный код, который будет легко ошибиться; не то, что я хочу попытаться обобщить в ответ SO.

3

В настоящее время это невозможно сделать в Ржавчине, и это никогда не станет возможным; это, однако, можно построить подобные абстракции как часть вашего признака:

trait Actor { 
    fn health(&self) -> Option<&Health>; 
} 

trait Health { } 

impl Actor for Monster { 
    fn health(&self) -> Option<&Health> { Some(self) } 
} 

impl Health for Monster { } 

impl Actor for Plant { 
    fn health(&self) -> Option<&Health> { None } 
} 

Ржавчина, как ожидается, чтобы получить негативные оценки в какой-то момент; когда это приходит, вы будете в состоянии иметь что-то вроде этого:

trait MaybeImplements<Trait: ?Sized> { 
    fn as_trait_ref(&self) -> Option<&Trait>; 
} 

macro_rules! impl_maybe_implements { 
    ($trait_:ident) => { 
     impl<T: $trait_> MaybeImplements<$trait_> for T { 
      fn as_trait_ref(&self) -> Option<&$trait_> { 
       Some(self) 
      } 
     } 

     impl<T: !$trait_> MaybeImplements<$trait_> for T { 
      fn as_trait_ref(&self) -> Option<&$trait_> { 
       None 
      } 
     } 
    } 
} 

impl_maybe_implements!(Health); 

trait Actor: MaybeImplements<Health> { 
} 

let health: Option<&Health> = actor.as_trait_ref(); 

Это уменьшит шаблонный от каждой реализации признака только один за черту, но этот этап еще не на нас. Тем не менее, вы могли бы взять на себя золотую середину из двух подходов:

trait MaybeImplements<Trait: ?Sized> { 
    fn as_trait_ref(&self) -> Option<&Trait>; 
} 

macro_rules! register_impl { 
    ($trait_:ident for $ty:ty) => { 
     impl MaybeImplements<$trait_> for $ty { 
      fn as_trait_ref(&self) -> Option<$trait_> { 
       Some(self) 
      } 
     } 
    } 

    (!$trait_:ident for $ty:ty) => { 
     impl MaybeImplements<$trait_> for $ty { 
      fn as_trait_ref(&self) -> Option<$trait_> { 
       None 
      } 
     } 
    } 
} 

register_impl!(Health for Monster); 
register_impl!(!Health for Plant); 

Поиграйте с различными способами обработки его до тех пор, пока не найдете то, что вам нравится! Возможности безграничны! (Поскольку Ржавчина Тьюринг-полной.)

Смежные вопросы