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…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...