2016-03-28 3 views
1

Как я могу избежать использования небезопасного кода в коде ниже? Он предназначен для участия в библиотеке сущностей и компонентов. В более общем плане, есть ли способ включить типы возврата в Rust так, чтобы компилятор знал внутри блока, что тип возврата и тип сопоставления одинаковы?Есть ли способ включить типы возврата, чтобы компилятор знал, что тип возврата и тип сопоставления совпадают?

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

ответ

2

Вы можете сделать это следующим образом:

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

Обратите внимание, что я также удалены явный срок службы из заголовка функции (в определении признака, тоже). Lifetime elision работает в этом случае, так как время жизни на выходе привязано к времени жизни (self).

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

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

Как записочка: Я думаю, что это было бы очень хорошо использовать плагин компилятора здесь, чтобы отметить некоторые элементы этой структуры в качестве субъектов в определении структуры.

+0

Ничего себе, это приятно знать, я планировал изучать плагины компилятора с помощью этого проекта :) И, возможно, вы уже знали об этом, но вы можете избежать приведения с Any :: downcast_ref (& self.health), а это даже очиститель. Благодаря! – Shien

+0

Возможно, это должен быть другой вопрос, но каким-либо образом это можно использовать в объектах признаков? Я имею в виду, я, наверное, должен был подумать об этом в первую очередь. – Shien

+0

@Shien Это сложно. Вам также нужно будет вернуть компонент как объект признаков. Но, как вы сказали, достаточно другого вопроса - может быть, на форуме пользователя Rust, а не SO ... –

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