当前位置: 首页 > news >正文

二十一、Rust 反射 获取类型

  不同于 java 中的反射,Rust 没有提供以往意义上的运行时反射,取而代之的是 “编译期反射”,如 类型分析、类型转换、类型签名。但即便如此,也已经能对 Rust元编程 提供很多助力了。

  这种操作,主要通过 Any 来实现,Rust 中提供了 Any Trait,所有类型(含自定义类型)都自动实现了该特征;因此,通过它我们可以进行一些类似反射的功能;

实际上,在 Rust 早期版本中是提供了 Reflection 的,但是在 14年移除了相关代码,原因是:

  1. 反射打破了原有的封装原则,能任意访问结构体内容,不安全;
  2. 反射的存在使得代码过于臃肿,移除后编译器可以简化很多;
  3. 反射功能设计的比较弱,开发者对于是否在未来的版本中还拥有反射功能存疑;

另一篇 关于Rust为何不引入 Runtime Reflection ,大致信息如下:

  1. 不一定非要使用反射来实现, Rust中可以有更好的实现:
  2. 派生宏和Trait之间的配合,可以将实现从运行时转移到编译时;
    例如,利用过程宏实现编译时反射,以实现依赖注入等功能 - https://github.com/dtolnay/reflect

至于保留 Any 的原因:

  1. 在调试范型类型相关代码时,有TypeId会更方便,更容易给出正确的错误提示;
  2. 有利于编译器作出代码的优化;

Any源码简读

参看Any源码文档:

  作为 &dyn Any (借用的 trait 对象),具有 isdowncast_ref 方法,可测试值是否为给定类型,并对类型的内部值进行引用;作为 &mut dyn Any,还有 downcast_mut 方法,用于获取内部值的 “可变引用” ;

  Box<dyn Any> 具有 downcast 方法,该方法尝试转换为 Box<T>;也有称 “类型具象化”。但需注意,&dyn Any 仅限于测试值是否为具体的类型,而不能用于测试类型是否实现了 Trait;

总结就是 std::any 起到的作用有4个:

  1. 获得变量的类型 a.type_id()
  2. 判断变量是否是指定的具体类型 a.is::<String>()TypeId::of::<String>() == a.type_id()
  3. 把any转换成指定类型 a.downcast_ref::<String>()
  4. 获取类型的名字 (_: &T) -> String { std::any::type_name::<T>().to_string() }

下面看一段 Any Trait 部分核心源码,可帮助更好理解 Any:

pub trait Any: 'static {fn type_id(&self) -> TypeId;
}// 获得变量的类型TypeId
// 为所有的T实现了Any
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: 'static + ?Sized> Any for T {fn type_id(&self) -> TypeId { TypeId::of::<T>() }
}// 判断变量是否是指定类型
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn is<T: Any>(&self) -> bool {// Get `TypeId` of the type this function is instantiated with.let t = TypeId::of::<T>();// Get `TypeId` of the type in the trait object.let concrete = self.type_id();// Compare both `TypeId`s on equality.t == concrete
}// 把any转换成指定类型
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {if self.is::<T>() {// SAFETY: just checked whether we are pointing to the correct typeunsafe {Some(&*(self as *const dyn Any as *const T))}} else {None}
}// 获取类型名字
pub const fn type_name<T: ?Sized>() -> &'static str {intrinsics::type_name::<T>()
}#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct TypeId {t: u64,
}

补充说明:

  1. Rust 中,所有拥有静态生命周期的类型都会实现Any,未来可能会考虑加入生命周期是非‘static的情况;

  2. Rust 中,所有类型都有一个全局唯一的标识 TypeId(A TypeId represents a globally unique identifier for a type);

  3. 这些 TypeId 都是通过调用 intrinsic 模块中定义的函数创建的,即 TypeId 的生成是由编译器的实现来决定的!

关于intrinsic 模块:

intrinsic 库函数是指:由编译器内置实现的函数,一般是具有如下特点的函数:

  • 与CPU架构相关性很大,必须利用汇编实现或者利用汇编才能具备最高性能的函数;
  • 和编译器密切相关的函数,由编译器来实现最为合适;
  • 具体实现 - https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/intrinsic.rs

Any基本使用

  • examples/main.rs
use std::any::{Any, TypeId};struct Person {pub name: String,
}/// 判断是否为指定类型-1
fn is_string(s: &dyn Any) -> bool {TypeId::of::<String>() == s.type_id() // 获取TypeId
}/// 判断是否为指定类型-2
fn check_string(s: &dyn Any) {if s.is::<String>() {println!("It's a string!");} else {println!("Not a string...");}
}/// 转换Any为特定类型
fn print_if_string(s: &dyn Any) {if let Some(ss) = s.downcast_ref::<String>() {println!("It's a string({}): '{}'", ss.len(), ss);} else {println!("Not a string...");}
}/// 获取类型的名字
/// 但需注意, 此方式获取的名字不唯一
///    如 type_name::<Option<String>> 可能返回 Option<String> 或 std::option::Option<std::string::String>
/// 同时, 编译器版本不同、可能返回值不同
fn get_type_name<T>(_: &T) -> String {std::any::type_name::<T>().to_string()
}fn main() {let p = Person { name: "John".to_string() };assert!(!is_string(&p));assert!(is_string(&p.name));check_string(&p);check_string(&p.name);print_if_string(&p);print_if_string(&p.name);println!("Type name of p: {}", get_type_name(&p));println!("Type name of p.name: {}", get_type_name(&p.name));
}
  • 输出如下
Not a string...
It's a string!
Not a string...
It's a string(4): 'John'
Type name of p: 0_any::Person
Type name of p.name: alloc::string::String

Any适用场景

  Rust 中的 Any 类似于 Java 中的 Object,可以传入任何拥有静态生命周期的类型;因此,当入参类型复杂,但后续又没有更多功能性操作时,就可以简化入参。例如,打印任何类型的值。

  • examples/1_print_any.rs
use std::any::Any;
use std::fmt::Debug;#[derive(Debug)]
struct MyType {name: String,age: u32,
}fn print_any<T: Any + Debug>(value: &T) {let value_any = value as &dyn Any;if let Some(string) = value_any.downcast_ref::<String>() {println!("String ({}): {}", string.len(), string);} else if let Some(MyType { name, age }) = value_any.downcast_ref::<MyType>() {println!("MyType ({}, {})", name, age)} else {println!("{:?}", value)}
}fn main() {let ty = MyType {name: "Rust".to_string(),age: 30,};let name = String::from("Rust");print_any(&ty);print_any(&name);print_any(&30);
}
  • 运行后输出
MyType (Rust, 30)
String (4): Rust
30

  如上所示,不论 String、MyType 自定义类型、还是内置的 i32 类型,都可以被打印,只要他们实现了 Debug Trait;这也可以认为是Rust 中、一种函数重载的方式,在读取一些结构复杂的配置时,也可以直接使用 Any。

最后总结

  Any Trait 并非常规意义上的 Reflection,而最多是编译期反射、且只启用了 “类型检查” 和 “类型转换”,并不检查结构的任意内容。

  Any 符合零成本抽象,因为Rust只会针对调用该函数的相关类型生成代码,并且返回的是编译器内部的类型ID,没有额外开销;甚至可以直接使用 TypeId::of::<String>,从而没有了 dyn any 的动态绑定开销。

  虽然 Rust 没有真正的 运行时 Reflection,但使用过程宏、仍可以实现大部分反射能够实现的功能,如上一节的 AOP 增强 !!

  

  

  就这样,bye bye ~

  

  

参考资料

  • https://www.jianshu.com/p/c4ef17bb1ca3
  • https://rust.ffactory.org/std/any/index.html
  • https://jasonkayzk.github.io/2022/11/24/Rust%E5%8F%8D%E5%B0%84%E4%B9%8BAny/

相关文章:

二十一、Rust 反射 获取类型

不同于 java 中的反射&#xff0c;Rust 没有提供以往意义上的运行时反射&#xff0c;取而代之的是 “编译期反射”&#xff0c;如 类型分析、类型转换、类型签名。但即便如此&#xff0c;也已经能对 Rust元编程 提供很多助力了。 这种操作&#xff0c;主要通过 Any 来实现&…...

Flutter Engine引擎概念

1.Flutter是Google提供的开源框架。 2.本身由C编写并兼容iOS(底层C)/Android(底层C)平台的FlutterEngine框架负责UI渲染、数据转移、调用DartVM虚拟机。 3.FlutterEngine框架由Skia图形库、Dart运行时、Flutter框架代码组成。Skia是用于图形绘制和文本显示的2D图形引擎库&#…...

【运行环境】加载资源的形式

相关资源&#xff1a;性能优化原则 1 加载资源的形式 html代码 媒体文件&#xff0c;如图片&#xff0c;视频等 javasccript css 2 加载资源的过程 DNS解析&#xff1a;域名-> ip地址 浏览器根据IP地址向服务器发送http 请求 服务器处理http 请求&#xff0c;并返回给浏览器…...

备战蓝桥杯Day40 - 第11届python组真题 - C跑步锻炼

一、题目描述 二、思路 1、使用datetime库中的方法可以很好的解决这个问题。 2、定义起始时间和结束时间&#xff0c;判断是否是周一或者是1号&#xff0c;结果res加上相应的里程数。 3、最后输出 res 即为本题答案。 三、代码实现 import datetimestart datetime.date(2…...

书生·浦语大模型第二期实战营第二课笔记和基础作业

来源&#xff1a; 作业要求:Homework - Demo 文档教程:轻松玩转书生浦语大模型趣味 Demo B站教程:轻松玩转书生浦语大模型趣味 Demo 1. 笔记 2.基础作业 2.1 作业要求 2.2 算力平台 2.3 新建demo目录&#xff0c;以及新建目录下的文件&#xff0c;下载模型参数 2.4 Intern…...

成功解决> 错误: 无效的源发行版:17

运行项目的时候出现下面的报错&#xff1a; Execution failed for task ‘:device_info_plus:compileDebugJavaWithJavac’. 错误: 无效的源发行版&#xff1a;17 原因&#xff1a;没有设置好自己项目的JDK版本 解决&#xff1a;1.检查自己项目的JDK版本 将自己的项目改为JDK 1…...

深度剖析:网络安全中的红蓝对抗策略

红蓝对抗 红蓝对抗服务方案 在蓝队服务中&#xff0c;作为攻击方将开展对目标资产的模拟入侵&#xff0c;寻找攻击路径&#xff0c;发现安全漏洞和隐患。除获取目标系统的关键信息&#xff08;包括但不限于资产信息、重要业务数据、代码或管理员账号等&#xff09;外&#x…...

Java异常处理之旅:解救迷失的程序员(二)

本系列文章简介&#xff1a; 在编程世界中&#xff0c;程序员们常常会遇到各种各样的问题和挑战。有时候&#xff0c;这些问题很容易解决&#xff0c;而有时候&#xff0c;它们却会让我们感到迷失和无助。 在这个旅程中&#xff0c;我们将探索Java异常处理的世界&#xff0c;解…...

网络安全介绍

网络安全是指网络系统的硬件、软件及其系统中的数据受到保护&#xff0c;不因偶然的或者恶意的原因而遭受到破坏、更改、泄露&#xff0c;系统能够连续可靠正常地运行&#xff0c;网络服务不中断。以下是一些网络安全相关的方面&#xff1a; 首先&#xff0c;随着科学技术的进…...

分享一个好看的APP下载分发页,App Store风格

分享一个好看的APP下载分发页&#xff0c;App Store风格 可以自动识别安卓和苹果哦&#xff01; 内容直接可以页面上修改&#xff0c;所见即所得 下图是一个真实截图&#xff0c;想要的留下评论哦&#xff01;...

C++ 获取数组大小、多维数组操作详解

获取数组的大小 要获取数组的大小&#xff0c;可以使用 sizeof() 运算符&#xff1a; 示例 int myNumbers[5] {10, 20, 30, 40, 50}; cout << sizeof(myNumbers);结果&#xff1a; 20为什么结果显示为 20 而不是 5&#xff0c;当数组包含 5 个元素时&#xff1f; 这…...

苹果电脑怎么彻底删除软件 苹果电脑卸载软件在哪里 cleanmymac x怎么卸载 mac废纸篓怎么删除

苹果电脑卸载软件的方法相对直观和简单&#xff0c;尤其是对于习惯使用Mac操作系统的用户来说。以苹果MacBook Pro为例&#xff0c;以下是卸载软件的详细步骤、使用方法、注意事项与建议。 一、卸载软件的详细步骤&#xff1a; 1. 打开Mac电脑&#xff0c;进入桌面&#xff0c…...

STM32F407 FSMC并口读取AD7606

先贴一下最终效果图.这个是AD7606并口读取数据一个周期后的数据结果. 原始波形用示波器看是很平滑的. AD7606不知为何就会出现干扰, 我猜测可能是数字信号干扰导致的. 因为干扰的波形很有规律. 这种现象基本上可以排除是程序问题. 应该是干扰或者数字信号干扰,或者是数字和模拟…...

WebGPU vs. 像素流

在构建 Bzar 之前&#xff0c;我们讨论过我们的技术栈是基于在云上渲染内容的像素流&#xff0c;还是基于使用设备自身计算能力的本地渲染技术。 由于这种选择会极大地影响项目的成本、可扩展性和用户体验&#xff0c;因此在开始编写一行代码之前&#xff0c;从一开始就采取正确…...

Windows下docker-compose部署DolphinScheduler

参照&#xff1a;快速上手 - Docker部署(Docker) - 《Apache DolphinScheduler v3.1.0 使用手册》 - 书栈网 BookStack 下载源文件 地址&#xff1a;https://dolphinscheduler.apache.org/zh-cn/download/3.2.1 解压到指定目录&#xff0c;进入apache-dolphinscheduler-xxx-…...

微服务项目sc2024通用Base工程

1. cloud-provider-payment8001 2.pom文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"ht…...

git学习 1

打开自己想要存放git仓库的文件夹&#xff0c;右键打开git bush&#xff0c;用git init命令建立仓库 用 ls -a(表示全都要看&#xff0c;包括隐藏的)可以看到git仓库 也可以用 git clone 接github链接&#xff08;点code选项里面会给链接&#xff0c;结尾是git的那个&#xf…...

HTML - 请你说一下如何阻止a标签跳转

难度级别:初级及以上 提问概率:55% a标签的默认语义化功能就是超链接,HTML给它的定位就是与外部页面进行交流,不过也可以通过锚点功能,定位到本页面的固定id区域去。但在开发场景中,又避免不了禁用a标签的需求,那么都有哪些方式可以禁用…...

【CV】ORB算法

1. ORB算法&#xff1a; 特点&#xff1a; 实现了旋转不变性、尺度不变性和计算效率高等特性。 旋转不变性&#xff1a; 通过计算关键点周围的梯度信息&#xff0c;确定关键点的主方向。将图像旋转到关键点的主方向&#xff0c;然后再提取BRIEF描述符&#xff0c;增强了旋转不…...

【算法】Cordic算法的原理及matlab/verilog应用

一、前言 单片机或者FPGA等计算能力弱的嵌入式设备进行加减运算还是容易实现&#xff0c;但是想要计算三角函数&#xff08;sin、cos、tan&#xff09;&#xff0c;甚至双曲线、指数、对数这样复杂的函数&#xff0c;那就需要费些力了。通常这些函数的计算需要通者查找表或近似…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...