Rust 如何实现反射?

问题描述 投票:0回答:2

Rust 具有

Any
的特点,但它也有“不为不使用的东西付费”的政策。 Rust是如何实现反射的?

我的猜测是 Rust 使用惰性标记。每个类型最初都是未分配的,但后来如果该类型的实例被传递给需要

Any
特征的函数,则该类型会被分配一个
TypeId

或者 Rust 可能会在其实例可能传递给该函数的每个类型上放置一个

TypeId
?我猜前者会很贵。

reflection rust
2个回答
94
投票

首先,Rust 没有反射;反射意味着您可以在运行时获取有关类型的详细信息,例如它实现的字段、方法、接口等。不能使用 Rust 执行此操作。您可以获得的最接近的是显式实现(或派生)提供此信息的特征。

每种类型都会在编译时分配一个

TypeId

。因为拥有全局有序的 ID 是“困难”的,所以 ID 是一个从类型定义和有关它所包含的 crate 的各种元数据的组合派生的整数。换句话说:它们没有以任何顺序分配,它们只是定义类型的各种信息的“散列”。 [1]
如果您查看 Any 特征

源代码,您将看到

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

实现在内存中浮动。实际发生的情况是,如果 
someone 编写了需要它的代码,编译器只会为类型的 Any 实现生成实际代码。 [3] 换句话说,如果您从不使用给定类型的 Any
 实现,编译器将永远不会生成它。
这就是 Rust 实现“不为你不使用的东西付费”的方式:如果你从未将给定类型传递为 
&AnyBox<Any>
,那么相关代码永远不会生成,也永远不会占用编译后的任何空间二进制。

[1]:令人沮丧的是,这意味着类型的

TypeId
可以
更改值
,具体取决于库的编译方式

,以至于将其编译为依赖项(而不是作为独立构建)导致
TypeId

要改变。


[2]:据我所知。我可能错了,但如果真是这样,我会真的感到惊讶。

[3]:对于 Rust 中的泛型来说,这通常是正确的。

有一项倡议在 Rust 中实施反射,当前的 crate 是一个概念验证项目。

您可以检查一下,看看是否符合您的需求。

https://docs.rs/crate/reflect/latest

0
投票

© www.soinside.com 2019 - 2024. All rights reserved.