Rust具有Any
特性,但它也有一个"不支付你不使用的东西"政策。 Rust如何实现反射?
我的猜测是Rust使用了懒惰标记。每个类型最初都是未分配的,但稍后如果将类型的实例传递给期望Any
特征的函数,则会为该类型分配TypeId
。
或者Rust可能会在其实例可能传递给该函数的每种类型上放置TypeId
?我想前者会很贵。
答案 0 :(得分:51)
首先,鲁斯特没有反思;反射意味着您可以在运行时获取有关类型的详细信息,例如字段,方法,它实现的接口,等。您不能使用Rust执行此操作。您可以获得的最接近的是显式实现(或派生)提供此信息的特征。
每种类型在编译时都会为其分配TypeId
。因为具有全局排序的ID是 hard ,所以ID是从类型的定义和关于包含它的包的各种元数据的组合派生的整数。换句话说:它们没有以任何顺序分配,它们只是哈希中用于定义类型的各种信息。 [1]
如果查看source for the Any
trait,您会看到Any
的单一实施:
impl<T: 'static + ?Sized > Any for T {
fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
}
(边界可以非正式地简化为“所有不借用其他东西的类型”。)
您还可以找到TypeId
的定义:
pub struct TypeId {
t: u64,
}
impl TypeId {
pub const fn of<T: ?Sized + 'static>() -> TypeId {
TypeId {
t: unsafe { intrinsics::type_id::<T>() },
}
}
}
intrinsics::type_id
是编译器识别的内部函数,给定一个类型,返回其内部类型ID。这个调用只是在编译时用文字整数类型ID替换;这里没有实际的电话。 [2]这就是TypeId
知道类型ID是什么的方式。然后,TypeId
只是这个u64
的包装器,用于隐藏用户的实现细节。如果您发现它在概念上更简单,您可以将类型TypeId
视为编译器在编译时知道的常量64位整数。
Any
从get_type_id
转发到此get_type_id
,意味着TypeId::of
真正只是将特征方法绑定到相应的Any
方法。只是确保如果您有TypeId
,就可以找到原始类型的Any
。
现在,Any
是针对大多数类型实现的,但这并不意味着所有这些类型实际上都有 Any
实现浮动在记忆中。实际发生的是,如果某人编写需要它的代码,编译器只会为类型的Any
实现生成实际代码。 [3]换句话说,如果你从不对给定类型使用&Any
实现,编译器将永远不会生成它。
这就是Rust履行“不支付你不使用的内容”的方式:如果你从未将给定类型作为Box<Any>
或TypeId
传递,那么相关的代码永远不会生成,也永远不会生成编译二进制文件中的任何空格。
[1]:令人沮丧的是,这意味着类型的TypeId
可以更改值,具体取决于 库的编译方式,以便编译它作为依赖项(而不是独立构建)导致routerCanDeactivate(nextInstruction, prevInstruction) { ... }
更改。
[2]:就我所知。我可能错了,但是如果是这样的话,我会非常感到惊讶。
[3]:这对于Rust中的泛型来说通常是。