2016-03-28 24 views
1

Wie kann ich die Verwendung von unsicherem Code im folgenden Code vermeiden? Es soll Teil einer Entity-Component-System-Bibliothek sein. Gibt es allgemein eine Möglichkeit, Rückgabetypen in Rust so zu aktivieren, dass der Compiler innerhalb des Blocks weiß, dass Rückgabetyp und übereinstimmender Typ identisch sind?Gibt es eine Möglichkeit, Rückgabetypen zu aktivieren, damit der Compiler weiß, dass der Rückgabetyp und der übereinstimmende Typ identisch sind?

use std::any::{Any, TypeId}; 
use std::mem; 

#[derive(Debug)] struct Health(f64); 
#[derive(Debug)] struct Position([f64; 3]); 

trait Entity { 
    fn get<'a, T: Any>(&self) -> Option<&'a T>; 
} 

#[derive(Debug)] 
struct Pig { 
    health: Health, 
    position: Position, 
} 

impl Entity for Pig { 
    fn get<'a, T: Any>(&self) -> Option<&'a T> { 
     if TypeId::of::<T>() == TypeId::of::<Health>() { 
      Some(unsafe {mem::transmute(&self.health)}) 
     } else if TypeId::of::<T>() == TypeId::of::<Position>() { 
      Some(unsafe {mem::transmute(&self.position)}) 
     } else { None } 
    } 
} 

fn main() { 
    let waddles = Pig { 
     health: Health(2.0), 
     position: Position([1.0, 2.0, 3.0]), 
    }; 

    println!("Waddles' Health: {:?}", waddles.get::<Health>()); 
} 

gist

Antwort

2

Sie können es wie folgt tun:

fn get<T: Any>(&self) -> Option<&T> { 
    if let Some(health) = Any::downcast_ref::<T>(&self.health) { 
     Some(&health) 
    } 
    else if let Some(position) = Any::downcast_ref::<T>(&self.position) { 
     Some(&position) 
    } else { 
     None 
    } 
} 

Bitte beachte, dass ich auch die explizite Lebenszeit von dem Funktionskopf entfernt (in der Eigenschaft Definition, auch). Lifetime Elision funktioniert in diesem Fall, da die Ausgabe-Lebensdauer an die Eingabe-Lebensdauer (self) gebunden ist.

Der obige Code ist ziemlich ausführlich und hat viel doppelten Code. So könnte es sinnvoll sein, ein einfaches Makro für sie zu schreiben:

macro_rules! entity_match { 
    ($self_:ident; $($entity:ident),*) => {{ 
     $(if let Some(inner) = Any::downcast_ref::<T>(&$self_.$entity) { 
      return Some(&inner); 
     })* 
     None 
    }} 
} 

impl Entity for Pig { 
    fn get<T: Any>(&self) -> Option<&T> { 
     entity_match!(self; health, position) 
    } 
} 

Als kleine Anmerkung: Ich denke, es wäre sehr schön sein, hier Compiler Plugins zu verwenden, einige Strukturkomponenten als Entitäten in der Struktur Definition zu markieren.

+0

Wow, das ist schön zu wissen, ich habe geplant, Compiler-Plugins mit diesem Projekt zu lernen :) Und du wusstest es wahrscheinlich schon, aber du kannst stattdessen die Besetzung mit Any :: downcast_ref (& self.health) vermeiden Reiniger. Vielen Dank! – Shien

+0

Das sollte wahrscheinlich eine andere Frage sein, aber können Sie das in Objektobjekten? Ich meine, daran hätte ich wahrscheinlich zuerst gedacht. – Shien

+0

@Shien Es ist schwierig. Sie müssten die Komponente auch als Merkmalsobjekt zurückgeben. Aber wie du schon sagtest, reicht es für eine andere Frage - vielleicht im Rust User Forum, anstatt SO ... –