rust宏
宏看起来和函数很像,只不过名称末尾有一个感叹号 ! 。
宏并不产生函数调用,而是展开成源码,并和程序的其余部分一起被编译。
Rust宏和C不同,Rust的宏会展开为抽象语法树(AST,abstract syntax tree),而不是直接字符串替换,这样就不会产生无法预料的优先级错误。
宏有两种:声明宏和过程宏
一、声明宏
(一)定义
声明宏通过macro_rules! 创建
每个声明宏都有一个名称和一条或多条规则。
每条规则都有两部分:一个匹配器(matcher),描述它匹配的句法;一个转换器(transcriber),描述成功匹配后将替代调用的句法。
匹配器和转换器都必须由定界符(delimiter)包围。
宏可以替换为表达式、语句、程序项、类型、模式
1.语法格式
macro_rules! macro_name {// 匹配器和转换器(pattern1) => { /* code1 */ };(pattern2) => { /* code2 */ };...
}
其中macro_name是宏的名字,pattern是匹配器,要匹配的模式,code是转换器,需要替换成的代码。可以看出跟match非常相似。
2.宏参数
在匹配器中使用$ IDENTIFIER : MacroFragSpec捕获宏参数
在转换器中使用$ IDENTIFIER表示宏参数
MacroFragSpec叫做指示符,表示参数的类型
有下列这些指示符
item条目, 例如函数、结构体、模块等
block代码块,
stmt语句(statement)
pat模式(pattern)
expr表达式
ty类型(type) 例如u8 u16
ident标识符 例如结构体、函数的名字
path路径 例如::std::mem::replace
meta元条目 例如 #[…] #![…]
tt标记树 token tree
vis可见性描述符 例如pub
literal 用于字面量
meta 元信息,例如 #[…]和 #![rust macro…] 属性
例子
macro_rules! create_function {// 此宏接受一个 `ident` 类型的参数,并创建一个名为 `$func_name` 的函数。($func_name:ident) => (fn $func_name() {// `stringify!` 宏把 `ident` 转换成字符串。println!("You called {:?}()", stringify!($func_name))})
}
// 借助上述宏来创建名为 `foo` 和 `bar` 的函数。
create_function!(foo);
create_function!(bar);
macro_rules! print_result {// 此宏接受一个 `expr` 类型的表达式,并将它作为字符串,连同其结果一起// 打印出来。// `expr` 指示符表示表达式。($expression:expr) => (// `stringify!` 把表达式*原样*转换成一个字符串。println!("{:?} = {:?}", stringify!($expression), $expression))
}
fn main() {foo();bar();print_result!(1u32 + 1);print_result!({let x = 1u32;x * x + 2 * x - 1});
}
3.宏重载
宏可以重载,从而接受不同的参数组合。也就是说,宏可以有多条规则。
类似于match代码块
例子
// 根据你调用它的方式,`test!` 将以不同的方式来比较 `$left` 和 `$right`。
macro_rules! test {// 参数不需要使用逗号隔开。// 参数可以任意组合!($left:expr; and $right:expr) => (println!("{:?} and {:?} is {:?}",stringify!($left),stringify!($right),$left && $right));($left:expr; or $right:expr) => (println!("{:?} or {:?} is {:?}",stringify!($left),stringify!($right),$left || $right));
}
fn main() {test!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32);test!(true; or false);
}
4.参数重复
在匹配器中使用 + 来表示可能出现一个或多个参数,使用 * 来表示出现零个或多个参数,用于可变参数列表。
* — 表示任意数量的重复元。
+ — 表示至少有一个重复元。
? — 表示一个可选的匹配段,可以出现零次或一次
例子
// `min!` 将求出任意数量的参数的最小值。
macro_rules! find_min {// 基本情形:($x:expr) => ($x);// `$x` 后面跟着至少一个 `$y,`($x:expr, $($y:expr),+) => (// 对 `$x` 后面的 `$y` 们调用 `find_min!`std::cmp::min($x, find_min!($($y),+)))
}
fn main() {println!("{}", find_min!(1u32));println!("{}", find_min!(1u32 + 2 , 2u32));println!("{}", find_min!(5u32, 2u32 * 3, 4u32));
}
5.作用域
#[macro_export] 表示只要导入了定义这个宏的 crate,该宏就应该是可用的。如果没有该属性,这个宏就不能被引入
例子
#[macro_export]
macro_rules! vec {( $( $x:expr ),* ) => {{let mut temp_vec = Vec::new();$(temp_vec.push($x);)*temp_vec}};
}
(二)使用
宏调用是在编译时执行宏,用执行结果替换该调用。
通过宏名后跟一个!和一个参数列表调用宏
比如
some_extension!(...)
some_extension!{...}
some_extension![...]
例子
// 作为表达式使用
let x = vec![1,2,3];
// 作为语句使用
println!("Hello!");
// 在模式中使用
macro_rules! pat {($i:ident) => (Some($i))
}
if let pat!(x) = Some(1) {assert_eq!(x, 1);
}
// 在类型中使用
macro_rules! Tuple {{ $A:ty, $B:ty } => { ($A, $B) };
}
type N2 = Tuple!(i32, i32);
// 作为程序项使用
thread_local!(static FOO: RefCell<u32> = RefCell::new(1));
// 作为关联程序项使用
macro_rules! const_maker {($t:ty, $v:tt) => { const CONST: $t = $v; };
}
trait T {const_maker!{i32, 7}
}
// 宏内调用宏
macro_rules! example {() => { println!("Macro call in a macro!") };
}
// 外部宏 `example` 展开后, 内部宏 `println` 才会展开.
example!();
二、过程宏
过程宏(procedural macros),更像函数。过程宏接收一些 代码,然后产生另一些代码,而非像声明式宏那样匹配对应模式然后以另一部分代码替换当前代码。
过程宏在编译时运行
过程宏必须在 crate类型为 proc-macro 的crate中定义。这种类型的crate总是链接编译器提供的 proc_macro crate。proc_macro crate提供了编写过程宏所需的各种类型和工具来让编写更容易。此crate主要包含了一个 TokenStream 类型。
注意: 使用Cargo时,定义过程宏的crate的配置文件里要做如下设置:
[lib]
proc-macro = true
过程宏有三种:
(一)类函数过程宏
类函数过程宏是使用宏调用运算符(!)调用的过程宏。
这种宏是由一个带有 proc_macro属性和 (TokenStream) -> TokenStream签名的 公有可见性函数定义。
输入 TokenStream 是由宏调用的定界符界定的内容,输出 TokenStream 将替换整个宏调用。
例如,下面的宏定义忽略它的输入,并将函数 answer 输出到它的作用域
#![crate_type = "proc-macro"]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro]
pub fn make_answer(_item: TokenStream) -> TokenStream {"fn answer() -> u32 { 42 }".parse().unwrap()
}
然后我们用它在一个二进制crate里打印 “42” 到标准输出。
extern crate proc_macro_examples;
use proc_macro_examples::make_answer;
make_answer!();
fn main() {println!("{}", answer());
}
(二)派生宏
派生宏为派生(derive)属性定义新输入。这类宏在给定输入结构体(struct)、枚举(enum)或联合体(union) token流的情况下创建新程序项。它们也可以定义派生宏辅助属性。
派生宏由带有 proc_macro_derive属性和 (TokenStream) -> TokenStream签名的公有可见性函数定义。
输入TokenStream 是带有 derive 属性的程序项的token流。输出TokenStream必须是一组程序项,然后将这组程序项追加到输入TokenStream中的那条程序项所在的模块或块中。
下面是派生宏的一个示例。它没有对输入执行任何有用的操作,只是追加了一个函数 answer。
#![crate_type = "proc-macro"]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_derive(AnswerFn)]
pub fn derive_answer_fn(_item: TokenStream) -> TokenStream {"fn answer() -> u32 { 42 }".parse().unwrap()
}
然后使用这个派生宏:
extern crate proc_macro_examples;
use proc_macro_examples::AnswerFn;
#[derive(AnswerFn)]
struct Struct;
fn main() {assert_eq!(42, answer());
}
派生宏辅助属性
派生宏可以将额外的属性添加到它们所在的程序项的作用域中。这些属性被称为派生宏辅助属性。这些属性是惰性的,它们存在的唯一目的是将这些属性在使用现场获得的属性值反向输入到定义它们的派生宏中。也就是说所有该宏的宏应用都可以看到它们。
定义辅助属性的方法是在 proc_macro_derive 宏中放置一个 attributes 键,此键带有一个使用逗号分隔的标识符列表,这些标识符是辅助属性的名称。
例如,下面的派生宏定义了一个辅助属性 helper,但最终没有用它做任何事情。
#![crate_type="proc-macro"]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_derive(HelperAttr, attributes(helper))]
pub fn derive_helper_attr(_item: TokenStream) -> TokenStream {TokenStream::new()
}
然后在一个结构体上使用这个派生宏:
#[derive(HelperAttr)]
struct Struct {#[helper]field: ()
}
(三)属性宏
属性宏用于自定义属性。虽然rust有许多内置属性,但有时我们需要自定义属性,这就是宏属性。
属性宏由带有 proc_macro_attribute属性和 (TokenStream, TokenStream) -> TokenStream签名的公有可见性函数定义。
签名中的第一个 TokenStream 是属性名称后面的定界token树(delimited token tree)(不包括外层定界符)。如果该属性作为裸属性(bare attribute)给出,则第一个 TokenStream 值为空。第二个 TokenStream 是程序项的其余部分,包括该程序项的其他属性。输出的 TokenStream 将此属性宏应用的程序项替换为任意数量的程序项。
例如,下面这个属性宏接受输入流并按原样返回,实际上对属性并无操作。
#![crate_type = "proc-macro"]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_attribute]
pub fn return_as_is(_attr: TokenStream, item: TokenStream) -> TokenStream {item
}
下面示例在编译时输出
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_attribute]
pub fn show_streams(attr: TokenStream, item: TokenStream) -> TokenStream {println!("attr: \"{}\"", attr.to_string());println!("item: \"{}\"", item.to_string());item
}
使用属性宏
extern crate my_macro;
use my_macro::show_streams;
// 示例: 基础函数
#[show_streams]
fn invoke1() {}
// out: attr: ""
// out: item: "fn invoke1() { }"// 示例: 带输入参数的属性
#[show_streams(bar)]
fn invoke2() {}
// out: attr: "bar"
// out: item: "fn invoke2() {}"// 示例: 输入参数中有多个token的
#[show_streams(multiple => tokens)]
fn invoke3() {}
// out: attr: "multiple => tokens"
// out: item: "fn invoke3() {}"// 示例:
#[show_streams { delimiters }]
fn invoke4() {}
// out: attr: "delimiters"
// out: item: "fn invoke4() {}"
相关文章:
rust宏
宏看起来和函数很像,只不过名称末尾有一个感叹号 ! 。 宏并不产生函数调用,而是展开成源码,并和程序的其余部分一起被编译。 Rust宏和C不同,Rust的宏会展开为抽象语法树(AST,abstract syntax treeÿ…...
性能测试之性能测试指标详解
前言 刚开始,以为做性能测试,就是做些脚本、参数化、关联,压起来之后,再扔出一个结果。 但实际上不止这些内容,还要加上性能分析,关注调优之后响应时间有多大的提升,TPS 有多大的提高…...
CustomNavBar 自定义导航栏视图
1. 创建偏好设置键 CustomNavBarTitlePreferenceKey.swift import Foundation import SwiftUI//State private var showBackButton: Bool true //State private var title: String "Title" //"" //State private var subtitle: String? "SubTitl…...
canal rocketmq
上篇文章canal 消费进度说到直接使用ClusterCanalConnector并发消费是有问题的,可以先用单点将canal事件发送到mq中,再由mq并发处理,另外mq还可以做到削峰的作用,让canal数据不至于阻塞。 使用队列,可以自己起一个单实…...
【数据库系统概论】第九章关系查询处理何查询优化
9.1查询处理 一:查询处理步骤 关系数据库管理系统查询处理可以分为4个阶段: 查询分析查询检查查询优化查询执行 (1)查询分析 任务:对查询语句进行扫描,分析词法、语法是否符合SQL语法规则 如果没有语…...
bp盐丘模型波场数值模拟matlab
波场数值模拟是地震勘探和地震学研究中常用的工具,而BP(Backpropagation)盐丘模型是一种用于地下介质成像的方法。如果您想在MATLAB中进行波场数值模拟,并结合BP盐丘模型进行地下成像,可以按照以下步骤进行:…...
结构体对齐规则
1.第一个成员在结构体变量偏移量为0的地址处。 2.其他成员变量对齐到某个数字(对齐数)的整数倍的地址处。(对齐数编译器默认的一个对齐数与该成员大小的较小值)注意:目前有且只有VS编译器有默认为8. 3.结构体总大小为最大对齐数的整数倍。 4.如果嵌套…...
css 如何让元素内部文本和外部文本 一块显示省略号
实际上还是有这样的需求的 <div class"container"><span>啊啊啊啊啊啊啊啊</span>你好啊撒撒啊撒撒撒撒啊撒撒撒撒撒说</div>还是有这样的需求的哦。 div.container {width: 200px;white-space: nowrap;text-overflow: ellipsis;overflow:…...
SQL语句-中级
一、Mysql软件使用 1.启动/停止Mysql服务器 任务管理器 cmd命令:以管理员的身份打开cmd命令行 net start mysql80//开启net stop mysql80//停止 2.连接与断开Mysql服务器 注意要在bin目录下执行:-u用户名root,-p密码 mysql -u root -p 可能出现的…...
巧用h2-database.jar连接数据库
文章目录 一 、概述二、实践三、解决办法 一 、概述 H2 Database是一个开源的嵌入式数据库引擎,采用java语言编写,不受平台的限制,同时H2 Database提供了一个十分方便的web控制台用于操作和管理数据库内容。H2 Database还提供兼容模式&#…...
136.只出现一次的数字
136. 只出现一次的数字 - 力扣(LeetCode) 给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题,且…...
mysql中遇到查询字段的别名与函数冲突问题
比如以下哎,我查询城市行业数量排名 select City, DENSE_RANK() over(ORDER BY COUNT(Id) DESC) rank, COUNT(Id) num,IndustrySubGroupName from base_companyinfo WHERE IndustrySubGroupName工业机器人 GROUP BY City 上面使用 DENSE_RANK() 函数来计算排名&am…...
直播获奖
题目描述 NOI2130 即将举行。为了增加观赏性, CCF 决定逐一评出每个选手的成 绩,并直播即时的获奖分数线。本次竞赛的获奖率为 𝑤% ,即当前排名前 𝑤% 的选手的最低成绩就是即时的分数线。 更具体地,…...
选择适合自身业务的HTTP代理有哪些因素决定?
相信对很多爬虫工作者和数据采集的企业来说,如何选购适合自己业务的HTTP代理是一个特别特别困扰的选题,市面上那么多HTTP代理厂商,好像这家有这些缺点,转头又看到另外一家的缺点,要找一家心仪的仿佛大海捞针。今天我们…...
1.3 do...while实现1+...100 for实现1+...100
思路:两个变量,一个变量存储数据之和,一个变量实现自增就行 do...while int i, s;i 1;s 0;do{s 1;i;} while (i < 100);cout << s << endl; for int i, j0;for (i 1; i < 100; i){j 1;}cout << j << …...
react数据管理之setState与Props
react数据管理之setState与Props setState调用原理 setState 是 React 中用于更新组件状态(state)的方法。它的调用原理可以分为以下几个步骤: 状态的改变:当调用 setState 时,React 会将新的状态对象与当前状态对象…...
如何保护我们的网络安全
保护网络安全是至关重要的,尤其是在今天的数字化时代。以下是一些保护网络安全的基本步骤: 1、使用强密码:使用包含字母、数字和特殊字符的复杂密码。不要在多个网站上重复使用相同的密码。定期更改密码。 2、启用双因素认证 (2FA)ÿ…...
springboot 制造装备物联及生产管理ERP系统
springboot 制造装备物联及生产管理ERP系统 liu1113625581...
Google zxing 生成带logo的二维码图片
环境准备 开发环境 JDK 1.8SpringBoot2.2.1Maven 3.2 开发工具 IntelliJ IDEAsmartGitNavicat15 添加maven配置 <dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.4.0</version> </…...
使用Python计算平面多边形间最短距离
要计算平面多边形间的最短距离,首先需要导入Excel表格中的多边形数据,然后使用GJK(Gilbert-Johnson-Keerthi)算法来判断两个多边形是否重叠。如果两个多边形不重叠,可以计算它们之间的最短距离。 以下是一个基本的Pyt…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...
