首页 文章

Rust如何实现反射?

提问于
浏览
40

Rust具有 Any 特性,但它也有"do not pay for what you do not use"策略 . Rust如何实现反射?

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

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

1 回答

  • 51

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

    每种类型在编译时都会分配给它 TypeId . 因为具有全局排序的ID很难,所以ID是从包含的类型's definition, and assorted metadata about the crate in which it'的组合派生的整数 . 换句话说:它们只是用于定义类型的各种信息的哈希值 . [1]

    如果查看source for the Any trait,您将看到 Any 的单个实现:

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

    (边界可以非正式地减少到"all types that aren't borrowed from something else" . )

    您还可以找到 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 实现生成实际代码 . [3]换句话说,如果您从未对给定类型使用 Any 实现,则编译器将永远不会生成它 .

    这就是Rust履行"do not pay for what do you not use"的方式:如果您从未将给定类型作为 &AnyBox<Any> 传递,则永远不会生成相关代码,也不会占用已编译二进制文件中的任何空间 .


    [1]:令人沮丧的是,这意味着类型的 TypeId 可以根据库的编译方式进行更改,以便将其编译为依赖项(而不是独立构建)会导致 TypeId 更改 .

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

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

相关问题