Rust如何实现反射?

Aka*_*gka 46 reflection rust

Rust具有这种Any特性,但它也有"不支付你不使用的"政策.Rust如何实现反射?

我的猜测是Rust使用了懒惰标记.每个类型最初都是未分配的,但是稍后如果将类型的实例传递给期望Any特征的函数,则为该类型分配一个TypeId.

或者Rust TypeId可能会在其实例可能传递给该函数的每种类型上放置一个?我想前者会很贵.

DK.*_*DK. 58

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

每种类型都TypeId在编译时分配给它.因为具有全局排序的ID 很难,所以ID是从类型的定义和关于包含它的包的各种元数据的组合得到的整数.换句话说:它们不是以任何顺序分配的,它们只是定义类型的各种信息的哈希.[1]

如果你查看特征来源Any,你会看到单个实现Any:

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

(边界可以非正式地缩减为"所有不借用其他东西的类型".)

您还可以找到以下定义TypeId:

pub struct TypeId {
    t: u64,
}

impl TypeId {
    pub const fn of<T: ?Sized + 'static>() -> TypeId {
        TypeId {
            t: unsafe { intrinsics::type_id::<T>() },
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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到改变.

[2]:就我所知.我可能错了,但如果是这样的话,我会感到非常惊讶.

[3]:这对于Rust中的泛型通常是正确的.

  • 有趣的是,[std :: any的文档](https://doc.rust-lang.org/std/any/)使用了"运行时反射"这个短语,尽管看起来他们所指的不是我们大多数人的意思是"运行时反射",因为这只能启用类型检查和类型转换,而不是检查任意结构的内容. (5认同)
  • 好的。通过反思,我的意思是 RTTI。 (2认同)
  • 对于有限形式的ad-hoc每种类型的特殊外壳,您可以使用不带"Any"的`TypeId`,而无需运行时成本. (2认同)
  • @Konrad:不。Rust 没有任何 RTTI,C++ 有(假设您没有禁用它)。Rust 没有等价于 `dynamic_cast`,因为它没有必要的 RTTI。 (2认同)