Rust如何实现反射?

s3fp2yjn  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(170)

Rust有Any特征,但它也有一个“不为你不使用的东西付费”的策略。Rust是如何实现反射的?
我猜Rust使用了延迟标记。每个类型最初都是未赋值的,但后来如果该类型的示例被传递给一个期望Any trait的函数,则该类型被赋值为TypeId
或者Rust在每个类型上都放置一个TypeId,它的示例可能会传递给该函数?我猜前者会很昂贵。

g52tjvyc

g52tjvyc1#

首先,Rust没有反射;反射意味着你可以在运行时获得关于类型的细节,比如字段,方法,它实现的接口,等等。你不能在Rust中做到这一点。你可以得到的最接近的是显式实现(或派生)一个提供这些信息的trait。
每个类型在编译时都会被分配一个TypeId。因为全局排序的ID是 * 硬 * 的,所以ID是一个整数,它来自于类型定义的组合,以及包含它的crate的各种元数据。换句话说:它们并没有以任何顺序赋值,它们只是定义类型的各种信息的 * 散列 *。
如果你看一下source for the Any trait,你会看到Any的单一实现:

impl<T: 'static + ?Sized > Any for T {
    fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
}

字符串
(The边界可以 * 非正式地 * 简化为“所有不从其他东西借用的类型”。
你也可以找到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;这里没有 actual 调用。[2]这就是TypeId如何知道类型的ID是什么。TypeId,那么,u64只是一个 Package 器,用来对用户隐藏实现细节。如果你觉得它在概念上更简单,你可以把类型的TypeId看作是一个常量64位整数,编译器在编译时只知道它。
Anyget_type_id转发到this,这意味着get_type_id * 实际上 * 只是将trait方法绑定到适当的TypeId::of方法。它只是为了确保如果您有Any,您可以找到原始类型的TypeId
现在,Any是为 * 大多数 * 类型实现的,但这并不意味着所有这些类型 * 实际上都有 * 一个Any实现在内存中浮动。实际发生的是,编译器只在 * 某人 * 编写需要它的代码时才为类型的Any实现生成实际代码。[3]换句话说,如果你从来没有为一个给定的类型使用Any实现,编译器将永远不会生成它。
这就是Rust如何实现“不为你不使用的东西付费”:如果你从来没有把一个给定的类型作为&AnyBox<Any>传递,那么相关的代码永远不会生成,也不会占用你编译的二进制文件中的任何空间。
[1]令人沮丧的是,这意味着一个类型的TypeId可以根据库的编译方式来改变值,以至于将其编译为依赖项(而不是独立构建)会导致TypeId s改变。
[2]我知道我可能错了,但如果是这样的话,我会感到非常惊讶。
[3]:Rust中的泛型 * 通常 * 是这样的。

相关问题