有没有同学记得我们一起挖了多少个坑?嗯…其实我自己也不记得了,今天我们再来挖一个特殊的坑,这个坑可以说是挖到根源了——元编程

元编程是编程领域的一个重要概念,它允许程序将代码作为数据,在运行时对代码进行修改或替换。如果你熟悉Java,此时是不是想到了Java的反射机制?没错,它就是属于元编程的一种。

反射

Rust也同样支持反射,Rust的反射是由标准库中的std::any::Any包支持的。

这个包中提供了以下几个方法

TypeId是Rust中的一种类型,它被用来表示某个类型的唯一标识。type_id(&self)这个方法返回变量的TypeId。

is()方法则用来判断某个函数的类型。

可以看一下它的源码实现

pub fn is<T: Any>(&self) -> bool {let t = TypeId::of::<T>();let concrete = self.type_id();t == concrete
}

可以看到它的实现非常简单,就是对比TypeId。

downcast_ref()downcast_mut()是一对用于将泛型T转换为具体类型的方法。其返回的类型是Option<&T>Option<&mut T>,也就是说downcast_ref()将类型T转换为不可变引用,而downcast_mut()将T转换为可变引用。

最后我们通过一个例子来看一下这几个函数的具体使用方法。

use std::any::{Any, TypeId};fn main() {let v1 = "Jackey";let mut a: &Any;a = &v1;println!("{:?}", a.type_id());assert!(a.is::<&str>());print_any(&v1);let v2: u32 = 33;print_any(&v2);
}fn print_any(any: &Any) {if let Some(v) = any.downcast_ref::<u32>() {println!("u32 {:x}", v);} else if let Some(v) = any.downcast_ref::<&str>() {println!("str {:?}", v);} else {println!("else");}
}

Rust的反射机制提供的功能比较有限,但是Rust还提供了宏来支持元编程。

到目前为止,宏对我们来说是一个既熟悉又陌生的概念,熟悉是因为我们一直在使用println!宏,陌生则是因为我们从没有详细介绍过它。

对于println!宏,我们直观上的使用感受是它和函数差不多。但两者之间还是有一定的区别的。

我们知道对于函数,它接收参数的个数是固定的,并且在函数定义时就已经固定了。而宏接收的参数个数则是不固定的。

这里我们说的宏都是类似函数的宏,此外,Rust还有一种宏是类似于属性的宏。它有点类似于Java中的注解,通常作为一种标记写在函数名上方。

#[route(GET, "/")]
fn index() {

route在这里是用来指定接口方法的,对于这个服务来讲,根路径的GET请求都被路由到这个index函数上。这样的宏是通过属于过程宏,它的定义使用了#[proc_macro_attribute]注解。而函数类似的过程宏在定义时使用的注解是#[proc_macro]

除了过程宏以外,宏的另一大分类叫做声明宏。声明宏是通过macro_rules!来声明定义的宏,它比过程宏的应用要更加广泛。我们曾经接触过的vec!就是声明宏的一种。它的定义如下:

#[macro_export]
macro_rules! vec {( $( $x:expr ),* ) => {{let mut temp_vec = Vec::new();$(temp_vec.push($x);)*temp_vec}};
}

下面我们来定义一个属于自己的宏。

自定义宏需要使用derive注解。(例子来自the book)

我们先来创建一个叫做hello_macro的lib库,只定义一个trait。

pub trait HelloMacro {fn hello_macro();
}

接着再创建一个子目录hello_macro_derive,在hello_macro_derive/Cargo.toml文件中添加依赖

[lib]
proc-macro = true[dependencies]
syn = "0.14.4"
quote = "0.6.3"

然后就可以在hello_macro_derive/lib.rs文件中定义我们自定义宏的功能实现了。

extern crate proc_macro;use crate::proc_macro::TokenStream;
use quote::quote;
use syn;#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {// Construct a representation of Rust code as a syntax tree// that we can manipulatelet ast = syn::parse(input).unwrap();// Build the trait implementationimpl_hello_macro(&ast)
}fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {let name = &ast.ident;let gen = quote! {impl HelloMacro for #name {fn hello_macro() {println!("Hello, Macro! My name is {}", stringify!(#name));}}};gen.into()
}

这里使用了两个crate:syn和quote,其中syn是把Rust代码转换成一种特殊的可操作的数据结构,而quote的作用则与它刚好相反。

可以看到,我们自定义宏使用的注解是#[proc_macro_derive(HelloMacro)],其中HelloMacro是宏的名称,在使用时,我们只需要使用注解#[derive(HelloMacro)]即可。

在使用时我们应该先引入这两个依赖

hello_macro = { path = "../hello_macro" }
hello_macro_derive = { path = "../hello_macro/hello_macro_derive" }

然后再来使用

use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;#[derive(HelloMacro)]
struct Pancakes;fn main() {Pancakes::hello_macro();
}

运行结果显示,我们能够成功在实现中捕获到结构体的名字。

总结

我们在本文中先后介绍了Rust的两种元编程:反射和宏。其中反射提供的功能能力较弱,但是宏提供的功能非常强大。我们所介绍的宏的相关知识其实只是皮毛,要想真正理解宏,还需要花更多的时间学习。

Rust入坑指南:万物初始相关推荐

  1. Rust 入坑指南:鳞次栉比 | CSDN 博文精选

    作者 | Jackyzhe 责编 | 屠敏 出品 | CSDN(ID:CSDNnews) 很久没有挖Rust的坑啦,今天来挖一些排列整齐的坑.没错,就是要介绍一些集合类型的数据类型."鳞次栉 ...

  2. Rust入坑指南:鳞次栉比

    很久没有挖Rust的坑啦,今天来挖一些排列整齐的坑.没错,就是要介绍一些集合类型的数据类型."鳞次栉比"这个标题是不是显得很有文化? 在Rust入坑指南:常规套路一文中我们已经介绍 ...

  3. Rust入坑指南:齐头并进(上)

    我们知道,如今CPU的计算能力已经非常强大,其速度比内存要高出许多个数量级.为了充分利用CPU资源,多数编程语言都提供了并发编程的能力,Rust也不例外. 聊到并发,就离不开多进程和多线程这两个概念. ...

  4. Rust入坑指南:朝生暮死

    今天想和大家一起把我们之前挖的坑再刨深一些.在Java中,一个对象能存活多久全靠JVM来决定,程序员并不需要去关心对象的生命周期,但是在Rust中就大不相同,一个对象从生到死我们都需要掌握的很清楚. ...

  5. Rust入坑指南:亡羊补牢

    如果你已经开始学习Rust,相信你已经体会过Rust编译器的强大.它可以帮助你避免程序中的大部分错误,但是编译器也不是万能的,如果程序写的不恰当,还是会发生错误,让程序崩溃.所以今天我们就来聊一聊Ru ...

  6. Rust入坑指南:核心概念

    如果说前面的坑我们一直在用小铲子挖的话,那么今天的坑就是用挖掘机挖的. 今天要介绍的是Rust的一个核心概念:Ownership.全文将分为什么是Ownership以及Ownership的传递类型两部 ...

  7. Rust入坑指南:千人千构

    坑越来越深了,在坑里的同学让我看到你们的双手! 前面我们聊过了Rust最基本的几种数据类型.不知道你还记不记得,如果不记得可以先复习一下.上一个坑挖好以后,有同学私信我说坑太深了,下来的时候差点崴了脚 ...

  8. Rust入坑指南:常规套路

    搭建好了开发环境之后,就算是正式跳进Rust的坑了,今天我就要开始继续向下挖了. 由于我们初来乍到 ,对Rust还不熟悉,所以我决定先走一遍常规套路. 变不变的变量 学习一门语言第一个要了解的当然就是 ...

  9. Rust入坑指南:坑主驾到

    欢迎大家和我一起入坑Rust,以后我就是坑主,我主要负责在前面挖坑,各位可以在上面看,有手痒的也可以和我一起挖.这个坑到底有多深?我也不知道,我是抱着有多深就挖多深的心态来的,下面我先跳了,各位请随意 ...

最新文章

  1. CKFinder 自定义文件路径扩展ConfigurationPathBuilder
  2. 物理光学11 衍射的基本概念与惠更斯原理
  3. C++菱形继承产生的问题和解决
  4. mysql 8.1初始密码_MySQL8.0安装之后查找默认密码
  5. 软件工程学习笔记《一》什么是软件工程
  6. 六个 Linux性能监控命令行工具
  7. c语言微秒级延迟程序,C语言中生产随机数及计算运行时间(微秒级)
  8. final、finally、finalize差异
  9. Raid0、Raid0+1、Raid1、Raid5四者的区别
  10. 使用autogen工具生成Makefile遇到问题解决思路
  11. linux小米随身wifi驱动下载,小米随身wifi驱动
  12. 科赫雪花c语言程序设计,用 C 语言画科赫雪花
  13. matlab simulink节点,ROS与Matlab语言入门教程-从Simulink中创建单独的ROS节点
  14. 【虹科技术分享】如何测试 DNS 服务器:DNS 性能和响应时间测试
  15. WPF——鼠标悬停在按钮时,只显示文字并高亮
  16. Malformed \uxxxx encoding问题的多种完美解决方法总结
  17. Unity 文字显示动画
  18. 微信小程序学习第6周————模块化
  19. Day 09 - Amazon Linux 2 上解决跨来源资源共用 (CORS) 与开机自动启动 uwsgi
  20. 有备无患!应届生CV算法岗面试指南

热门文章

  1. 2404 Super Prime(欧拉筛素数)
  2. 三feng云,免费主机
  3. 基于FPGA设计的音乐播放器
  4. 计算机视觉教程核心版(八)卷积神经网络各种层
  5. Android设备管理
  6. Facebook的公共主页怎么发帖看起来更专业
  7. 利用Android传感器开发指南针
  8. C#中var关键字 //作者:courageously
  9. 如何获得U盘的详细信息
  10. 南中轴爆发!南苑湿地公园绿化过半 将再现“南囿秋风”