Rust具有 Any 特性,但它也有"do not pay for what you do not use"策略 . Rust如何实现反射?
Any
我的猜测是Rust使用了懒惰标记 . 每个类型最初都是未分配的,但是稍后如果将类型的实例传递给期望 Any 特征的函数,则会为该类型分配 TypeId .
TypeId
或者Rust可能会在其实例可能传递给该函数的每种类型上放置 TypeId ?我想前者会很贵 .
首先,鲁斯特没有反思;反射意味着您可以在运行时获取有关类型的详细信息,例如字段,方法,它实现的接口等 . 您不能使用Rust执行此操作 . 您可以获得的最接近的是显式实现(或派生)提供此信息的特征 .
每种类型在编译时都会分配给它 TypeId . 因为具有全局排序的ID很难,所以ID是从包含的类型's definition, and assorted metadata about the crate in which it'的组合派生的整数 . 换句话说:它们只是用于定义类型的各种信息的哈希值 . [1]
如果查看source for the Any trait,您将看到 Any 的单个实现:
impl<T: 'static + ?Sized > Any for T { fn get_type_id(&self) -> TypeId { TypeId::of::<T>() } }
(边界可以非正式地减少到"all types that aren't borrowed from something else" . )
您还可以找到 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位整数 .
intrinsics::type_id
u64
Any 从 get_type_id 转发到此,意味着 get_type_id 实际上只是将特征方法绑定到适当的 TypeId::of 方法 . 它只是确保如果你有一个 Any ,你可以找到原始类型的 TypeId .
get_type_id
TypeId::of
现在, Any 已针对大多数类型实现,但这并不意味着所有这些类型实际上都在内存中浮动 Any 实现 . 实际发生的是,如果有人编写需要它的代码,编译器只为类型的 Any 实现生成实际代码 . [3]换句话说,如果您从未对给定类型使用 Any 实现,则编译器将永远不会生成它 .
这就是Rust履行"do not pay for what do you not use"的方式:如果您从未将给定类型作为 &Any 或 Box<Any> 传递,则永远不会生成相关代码,也不会占用已编译二进制文件中的任何空间 .
&Any
Box<Any>
[1]:令人沮丧的是,这意味着类型的 TypeId 可以根据库的编译方式进行更改,以便将其编译为依赖项(而不是独立构建)会导致 TypeId 更改 .
[2]:就我所知 . 我可能错了,但如果是这样的话,我会感到非常惊讶 .
[3]:这对于Rust中的泛型通常是正确的 .
1 回答
首先,鲁斯特没有反思;反射意味着您可以在运行时获取有关类型的详细信息,例如字段,方法,它实现的接口等 . 您不能使用Rust执行此操作 . 您可以获得的最接近的是显式实现(或派生)提供此信息的特征 .
每种类型在编译时都会分配给它
TypeId
. 因为具有全局排序的ID很难,所以ID是从包含的类型's definition, and assorted metadata about the crate in which it'的组合派生的整数 . 换句话说:它们只是用于定义类型的各种信息的哈希值 . [1]如果查看source for the Any trait,您将看到
Any
的单个实现:(边界可以非正式地减少到"all types that aren't borrowed from something else" . )
您还可以找到
TypeId
的定义: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履行"do not pay for what do you not use"的方式:如果您从未将给定类型作为
&Any
或Box<Any>
传递,则永远不会生成相关代码,也不会占用已编译二进制文件中的任何空间 .[1]:令人沮丧的是,这意味着类型的
TypeId
可以根据库的编译方式进行更改,以便将其编译为依赖项(而不是独立构建)会导致TypeId
更改 .[2]:就我所知 . 我可能错了,但如果是这样的话,我会感到非常惊讶 .
[3]:这对于Rust中的泛型通常是正确的 .