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位.

reallyget_type_id的绑定方法是正确的.它只是为了确保如果你有一个Any,你可以找到原始类型的TypeId.

现在,Any是为most个类型实现的,但这并不意味着所有这些类型都是在内存中浮动的Any实现.实际上,如果someone编写需要的代码,编译器只会为类型的Any实现生成实际代码.[3] 换句话说,如果您从未对给定类型使用Any实现,编译器将永远不会生成它.

这就是Rust如何实现"不要为你不使用的东西付费":如果你从未将给定的类型传递为&AnyBox<Any>,那么关联的代码永远不会生成,也永远不会占用编译的二进制文件中的任何空间.


[1] :令人沮丧的是,这意味着一个类型的TypeId可能取决于库被编译的具体情况,以至于将其编译为依赖项(而不是独立构建)会导致TypeId发生变化.

[2] :据我所知.我可能错了,但如果是这样的话,我会大吃一惊.

[3] :对于 rust 病中的仿制药来说,这是generally个事实.

Rust相关问答推荐

移植带有可变borrow 的C代码-卸载期间错误(nappgui示例)

从Rust调用C++虚拟方法即使在成功执行之后也会引发Access违规错误

如何最好地并行化修改同一Rust向量的多个切片的代码?

这个规则关于或模式到底是什么意思?如果表达片段的类型与p_i|q_i...&q;不一致,就会形成

为什么这是&q;,而让&q;循环是无限循环?

用 rust 蚀中的future 展望 struct 的future

当T不执行Copy时,如何返回Arc Mutex T后面的值?

如何高效地将 struct 向量中的字段收集到单独的数组中

如何在函数中返回自定义字符串引用?

装箱特性如何影响传递给它的参数的生命周期 ?(举一个非常具体的例子)

RUST 中的读写器锁定模式

提取指向特征函数的原始指针

Rust 重写函数参数

write_buffer 不写入缓冲区而是输出零 WGPU

当推送到 HashMap 中的 Vector 时,类型 `()` 无法取消引用

为什么具有 Vec 变体的枚举没有内存开销?

在 Rust 中为泛型 struct 编写一次特征绑定

Rustlings 切片原语

实现不消费的迭代器

匹配结果时的简洁日志(log)记录