Rust如何实现反射?

时间:2016-04-05 03:46:44

标签: reflection rust

Rust具有Any特性,但它也有一个"不支付你不使用的东西"政策。 Rust如何实现反射?

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

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

1 个答案:

答案 0 :(得分:51)

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

每种类型在编译时都会为其分配TypeId。因为具有全局排序的ID是 hard ,所以ID是从类型的定义和关于包含它的包的各种元数据的组合派生的整数。换句话说:它们没有以任何顺序分配,它们只是哈希中用于定义类型的各种信息。 [1]

如果查看source for the Any trait,您会看到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位整数。

Anyget_type_id转发到此get_type_id,意味着TypeId::of 真正只是将特征方法绑定到相应的Any方法。只是确保如果您有TypeId,就可以找到原始类型的Any

现在,Any是针对大多数类型实现的,但这并不意味着所有这些类型实际上都有 Any实现浮动在记忆中。实际发生的是,如果某人编写需要它的代码,编译器只会为类型的Any实现生成实际代码。 [3]换句话说,如果你从不对给定类型使用&Any实现,编译器将永远不会生成它。

这就是Rust履行“不支付你不使用的内容”的方式:如果你从未将给定类型作为Box<Any>TypeId传递,那么相关的代码永远不会生成,也永远不会生成编译二进制文件中的任何空格。

[1]:令人沮丧的是,这意味着类型的TypeId可以更改值,具体取决于 库的编译方式,以便编译它作为依赖项(而不是独立构建)导致routerCanDeactivate(nextInstruction, prevInstruction) { ... }更改。

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

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