一、反射
反射这个术语,现在基本烂大街了,可是当初刚刚出来时,可是一个令人眼前一亮的好东西啊。特别是在Java和c#上,它起到了一种非常棒的设计体验。相比之下,c++和C简单就是设计实现上的一种不友好的朋友。那么什么是反射呢?说直白一点就就是在运行时可以动态获得类的信息或者说动态调用类的对象的一种机制。
Rust看来也想在一方面分一杯羹,可惜的是,它的替代对象是C/C++,所以支持的不能很全面,或者说一个蹩脚的支持。它只支持编译时反射即对 'static 生命周期的变量(常量)进行反射!
二、Rust对反射的支持
刚刚提到过,Rust只能是一种编译时反射,所以它在应用的时候儿一定会有很多限制。在Rust中,就有一个Any模块来实现反射,它主要实现以下几个功能:
1、动态获取变量的类型(TypeId),这个在c++中也有类似机制。
2、变量类型的判断。
3、将any动态转换为指定类型。
4、动态得到类型名。
有的资料上讲,和绿色线程一样,早期Rust也是有反射功能的。但是反射的缺点和优点一样的明显,所以就去除了。any的用法其实和c++中的某些用法其实是高度类似的,这里就不展开详述了,同样的家伙什儿,谁也高明不到哪儿去。
三、Rust反射的源码
看一看在Rust源码中反射的定义:
//any.rs
//这里限定了Any的生命周期
#[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;
}
//获得Typeid
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: 'static + ?Sized> Any for T {
fn type_id(&self) -> TypeId {
TypeId::of::<T>()
}
}
//Any的具体实现接口
impl dyn Any {
//判断指定类型
#[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 (`self`).
let concrete = self.type_id();
// Compare both `TypeId`s on equality.
t == concrete
}
//动态转换指定类型
#[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, and we can rely on
// that check for memory safety because we have implemented Any for all types; no other
// impls can exist as they would conflict with our impl.
unsafe { Some(&*(self as *const dyn Any as *const T)) }
} else {
None
}
}
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
// SAFETY: just checked whether we are pointing to the correct type, and we can rely on
// that check for memory safety because we have implemented Any for all types; no other
// impls can exist as they would conflict with our impl.
unsafe { Some(&mut *(self as *mut dyn Any as *mut T)) }
} else {
None
}
}
}
///
// Extension methods for Any trait objects.
///
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Debug for dyn Any {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("Any")
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct TypeId {
t: u64,
}
//typeId的具体定义
impl TypeId {
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_type_id", issue = "77125")]
pub const fn of<T: ?Sized + 'static>() -> TypeId {
TypeId { t: intrinsics::type_id::<T>() }
}
}
//得到名字
#[stable(feature = "type_name", since = "1.38.0")]
#[rustc_const_unstable(feature = "const_type_name", issue = "63084")]
pub const fn type_name<T: ?Sized>() -> &'static str {
intrinsics::type_name::<T>()
}
#[unstable(feature = "type_name_of_val", issue = "66359")]
#[rustc_const_unstable(feature = "const_type_name", issue = "63084")]
pub const fn type_name_of_val<T: ?Sized>(_val: &T) -> &'static str {
type_name::<T>()
}
四、Rust反射的应用
在上面提到了Rust的几个用法,其实最典型的例子是Rust本身是不支持重载的,那么实现重载怎么办呢?就可以用这个std::any。下面看几个小例子:
//得到Type_id
use std::any::{Any, TypeId};
fn is_string(str: &dyn Any) -> bool {
TypeId::of::<String>() == str.type_id()
}
//判断类型
use std::any::Any;
fn is_string(str: &dyn Any) {
if str.is::<String>() {
println!("is string!");
} else {
println!("not string!");
}
}
//获取类型名
//注意版本不同可能名字略有不同,同时相关的类型可能名字也有所不同
assert_eq!(
std::any::type_name::<Option<String>>(),
"core::option::Option<alloc::string::String>",
);
再看一下《Rust Primer》的一个例子:
use std::any::Any;
use std::fmt::Debug ;
fn load_config<T:Any+Debug>(value: &T) -> Vec<String>{
let mut cfgs: Vec<String>= vec![];
let value = value as &Any;
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()]);
}
这个实例其实就是解决一个类似多配置文件的加载的重载函数的处理情况。通过反射来实现动态转换的过程,从而忽略掉不同参数配置产生的不同的结果。
五、总结
正如文档上所说,Rust的反射是不能用于接口断言的。这个一定要注意,对于Rust中的反射,就当成一个鸡肋用就可以了。鸡肋,用到位置上,仍然是很香的。正如管理学上所说,没有一个不好的员工,只有一个不会用人的领导。共勉!
努力吧,归来的少年!