Rust具有Any特性,但它也有“不使用的不付费”策略。Rust如何实现反射?
我的猜测是Rust使用了惰性标记。每种类型最初都是未赋值的,但后来如果将该类型的实例传递给需要Any特征的函数,则会为该类型分配一个TypeId。
或者可能Rust在其实例可能传递给该函数的每个类型上都放置了一个TypeId?我想前者会很贵。
发布于 2016-04-05 12:09:38
首先,Rust没有反射;反射意味着您可以在运行时获取有关类型的详细信息,如它实现的字段、方法、接口等。您所能得到的最接近的方法是显式地实现(或派生)提供此信息的特征。
每种类型都会在编译时获得一个分配给它的TypeId。因为很难获得全局排序的ID,所以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实现生成实际代码。3换句话说,如果您从未对给定类型使用过Any实现,编译器将永远不会生成它。
这就是Rust如何实现“不为您不使用的东西付费”:如果您从未将给定类型作为&Any或Box<Any>传递,则不会生成相关的代码,也不会占用已编译的二进制文件中的任何空间。
1:令人沮丧的是,这意味着类型的TypeId可以确切地根据库的编译方式更改值,以至于将其编译为依赖项(而不是独立的构建)会导致TypeIds更改。
他说:据我所知。在这一点上我可能是错的,但如果是这样的话,我会感到非常惊讶。
3:这通常适用于Rust中的泛型。
https://stackoverflow.com/questions/36416773
复制相似问题