Rust 有Any
种特性,但它也有一个"不使用就不付费"的政策.Rust如何实现反射?
我猜Rust使用惰性标记.每个类型最初都是未赋值的,但如果该类型的一个实例被传递给一个预期为Any
特征的函数,则该类型将被赋值为TypeId
.
或者Rust会给每个可能传递给该函数的实例类型加上TypeId
?我想前者会很贵.
Rust 有Any
种特性,但它也有一个"不使用就不付费"的政策.Rust如何实现反射?
我猜Rust使用惰性标记.每个类型最初都是未赋值的,但如果该类型的一个实例被传递给一个预期为Any
特征的函数,则该类型将被赋值为TypeId
.
或者Rust会给每个可能传递给该函数的实例类型加上TypeId
?我想前者会很贵.
首先, rust 没有反射;反射意味着您可以在运行时获取有关类型的详细信息,比如它实现的字段、方法和接口,etc.您可以使用Rust来实现这一点.你能得到的最接近的结果是明确实现(或派生)一个提供这种信息的特征.
每个类型在编译时都会被分配一个TypeId
.因为全局排序的ID是hard,所以ID是一个整数,它是从类型定义和包含它的 crate 的各种元数据的组合中派生出来的.换句话说:它们不是按任何顺序分配的,它们只是定义类型的各种信息中的hashes位.[1]
如果你看看source for the Any
trait,你会看到Any
的单一实现:
impl<T: 'static + ?Sized > Any for T {
fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
}
(边界可以减少到informally,即"所有不是从其他地方borrow 的类型".)
你也可以找到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位整数,编译器在编译时只有knows位.
从really到get_type_id
的绑定方法是正确的.它只是为了确保如果你有一个Any
,你可以找到原始类型的TypeId
.
现在,Any
是为most个类型实现的,但这并不意味着所有这些类型都是在内存中浮动的Any
实现.实际上,如果someone编写需要的代码,编译器只会为类型的Any
实现生成实际代码.[3] 换句话说,如果您从未对给定类型使用Any
实现,编译器将永远不会生成它.
这就是Rust如何实现"不要为你不使用的东西付费":如果你从未将给定的类型传递为&Any
或Box<Any>
,那么关联的代码永远不会生成,也永远不会占用编译的二进制文件中的任何空间.
[1] :令人沮丧的是,这意味着一个类型的TypeId
可能取决于库被编译的具体情况,以至于将其编译为依赖项(而不是独立构建)会导致TypeId
发生变化.
[2] :据我所知.我可能错了,但如果是这样的话,我会大吃一惊.
[3] :对于 rust 病中的仿制药来说,这是generally个事实.