Rust std-any 模块详解

2020-06-20  本文已影响0人  生若夏花_1ad0

1. 简介

反射reflection意味着可以在运行时获得类型的所有详细信息,包括字段方法等,并可以进行替换。rust只有“compile-time reflection”,和java不同,java运行在虚拟机之上,拥有“runtime reflection”,关于rust为什么不引入运行时反射,有很多相关讨论,在此不进行赘述

rust目前的反射功能比较弱,只有any可以算是起到了部分反射的功能,不过社区有人实现了利用过程宏reflect实现的编译时反射功能,以实现依赖注入等反射功能。

std:any起到的作用有4个

2. 关键内容

// 每一个类型都有自己的全局唯一的标志符`
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]`
#[stable(feature = "rust1", since = "1.0.0")]`
pub struct TypeId {
    t: u64,
}
// 调用了intrinsic库里的函数(ps,forget,size_of也在里面),是内建的类型,方便语言内部功能的实现。
impl TypeId {
  #[stable(feature = "rust1", since = "1.0.0")]
  #[rustc_const_unstable(feature="const_type_id")]
  pub const fn *of*<T: ?Sized + *'static*>() -> TypeId {
        TypeId {
            #[cfg(not(bootstrap))]
            t: intrinsics::type_id::<T>(),
        }
    }
}
// 所有拥有静态生命周期的类型都会实现Any,未来可能会考虑加入生命周期是非‘static的情况
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Any: 'static {
    #[stable(feature = "get_type_id", since = "1.34.0")]
    fn type_id(&self) -> TypeId;

// 主要内容1.获得变量的类型TypeId
// 为所有的T实现了Any
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: 'static + ?Sized > Any for T {
    fn type_id(&self) -> TypeId { TypeId::of::<T>() }
}}
 

// 主要内容2.判断变量是否是指定类型
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn is<T: Any>(&self) -> bool {
    // Get `TypeId` of the type this function is instantiated with.
    let t = TypeId::of::<T>();

    // Get `TypeId` of the type in the trait object.
    let concrete = self.type_id();

    // Compare both `TypeId`s on equality.
    t == concrete
}
 

// 主要内容3.把any转换成指定类型
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
    if self.is::<T>() {
        // SAFETY: just checked whether we are pointing to the correct type
        unsafe {
            Some(&*(self as *const dyn Any as *const T))
        }
    } else {
        None
    }
}

// 主要内容4.获取类型名字
pub const fn type_name<T: ?Sized>() -> &'static str {
    intrinsics::type_name::<T>()
}

关于type_id的生成,rust最早的版本使用的是

self.const_u64(self.tcx.type_id_hash(substs.type_at(0)))

现在的版本使用的是

let ty_name = self
.tcx
.const_eval_instance(ty::ParamEnv::reveal_all(), instance, None)
.unwrap();
OperandRef::from_const(self, ty_name).immediate_or_packed_pair(self)

具体地址

https://github.com/rust-lang/rust/blob/8a87b945b27b5670ac5ed665bbb0fccc1b88a0a0/src/librustc_codegen_llvm/intrinsic.rs#L231

3. 使用示例

//1 获取Type_id
use std::any::{Any, TypeId};
fn is_string(s: &dyn Any) -> bool {
     TypeId::of::<String>() == s.type_id()
}
//2.判断是否是指定类型
use std::any::Any;
fn is_string(s: &dyn Any) {
     if s.is::<String>() {
         println!("It's a string!");
     } else {
         println!("Not a string...");
     }
}
//3.转换any为特定类型
use std::any::Any;
fn print_if_string(s: &dyn Any) {
     if let Some(string) = s.downcast_ref::<String>() {
         println!("It's a string({}): '{}'", string.len(), string);
     } else {
         println!("Not a string...");
     }
} 
//4.获取类型的名字
//通过此函数获得的名字不唯一,比如type_name::<Option<String>>()可能返回"Option<String>"
//或"std::option::Option<std::string::String>",同时编译器版本不同返回值可能不同
assert_eq!(
    std::any::type_name::<Option<String>>(),
    "core::option::Option<alloc::string::String>",
);

4. 适用场景

use std::any::Any;
use std::fmt::Debug ;

fn load_config(value: &dyn Any) -> Vec<String>{
    let mut cfgs: Vec<String>= vec![];
    match value.downcast_ref::<String>() {
        Some(cfp) => cfgs.push(cfp.clone()),
        None => (),
    };

    match value.downcast_ref::<Vec<String>>() {
        Some(v) => cfgs.extend_from_slice(&v),
        None =>(),
    }

    if cfgs.len() == 0 {
        panic!("No Config File");
    }
    cfgs
}

fn main() {
    let cfp = "/etc/wayslog.conf".to_string();
    assert_eq!(load_config(&cfp), vec!["/etc/wayslog.conf".to_string()]);
    let cfps = vec!["/etc/wayslog.conf".to_string(),
                    "/etc/wayslog_sec.conf".to_string()];
    assert_eq!(load_config(&cfps),
               vec!["/etc/wayslog.conf".to_string(),
                    "/etc/wayslog_sec.conf".to_string()]);
}


5. 额外的话

上一篇 下一篇

猜你喜欢

热点阅读