有没有办法打开 return 类型,以便编译器知道 return 类型和匹配的类型相同?

Is there a way to switch on return types so that the compiler knows that the return type and matched type are the same?

如何避免在下面的代码中使用不安全的代码?它意味着成为实体组件系统库的一部分。更一般地说,有没有一种方法可以在 Rust 中打开 return 类型,编译器会在块内知道 return 类型和匹配类型相同?

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

你可以这样做:

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

请注意,我还从函数头中删除了显式生命周期(也在特征定义中)。生命周期省略在这种情况下有效,因为输出生命周期绑定到输入生命周期 (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)
    }
}

注意一点:我认为在这里使用编译器插件会非常好,将一些结构成员标记为结构定义中的实体。