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

CTF-web: Rust 的过程宏

Rust 的过程宏(Procedural Macros)是一种强大的元编程工具,允许你在编译时对代码进行操作和生成。与属性宏和派生宏不同,过程宏可以接收并处理任意 Rust 代码,生成新的代码片段。这里有一个简单的例子来说明 Rust 的过程宏。

假设你想创建一个过程宏来生成一个函数,该函数返回一个固定的字符串。我们可以按如下步骤进行:

  1. 创建一个新的 Rust 库项目

    cargo new my_proc_macro --lib
    cd my_proc_macro
    
  2. 配置 Cargo.toml
    Cargo.toml 中,我们需要启用过程宏支持,并添加必要的依赖项:

    [package]  
    name = "my_proc_macro"  
    version = "0.1.0"  
    edition = "2018"  [dependencies]  
    syn = { version = "1.0", features = ["full"] }  
    quote = "1.0"  
    proc-macro2 = "1.0"  [lib]  
    proc-macro = true
    
  3. 编写过程宏代码
    编辑 lib.rs 文件,编写我们的过程宏:

    extern crate proc_macro;
    use proc_macro::TokenStream;
    use quote::quote;
    use syn::{parse_macro_input, ItemFn};#[proc_macro_attribute]
    pub fn hello_fn(_attr: TokenStream, item: TokenStream) -> TokenStream {// 解析输入的函数let input = parse_macro_input!(item as ItemFn);let name = &input.sig.ident;// 生成新的代码let expanded = quote! {pub fn #name() -> &'static str {"Hello, world!"}};// 把生成的代码返回给编译器TokenStream::from(expanded)
    }
    
  4. 使用过程宏
    创建一个新的二进制项目来使用这个宏:

    cargo new my_app
    cd my_app
    

    更新 Cargo.toml 以包含我们刚刚创建的过程宏库:

    [package]  
    name = "my_app"  
    version = "0.1.0"  
    edition = "2018"  
    [dependencies]  
    my_proc_macro = { path = "../my_proc_macro" }
    

    编写使用这个过程宏的代码:

    use my_proc_macro::hello_fn;#[hello_fn]
    fn greet() {}fn main() {println!("{}", greet());
    }
    
  5. 运行程序
    现在你可以编译并运行这个程序:

    cargo run
    

    你将会看到输出:

    Hello, world!
    

这个简单的例子展示了如何创建和使用一个过程宏。这个过程宏 hello_fn 接受一个函数,并生成一个返回固定字符串 "Hello, world!" 的函数。实际中,过程宏可以用来生成更复杂的代码,提供编译时的代码验证和生成功能。

过程宏 #[hello_fn]。这像是你给 greet 函数贴上了一个标签,告诉编译器:“嘿!我希望这个函数返回的是 'Hello, world!',而不仅仅是一个普通的空函数。

  1. 当编译器遇到 #[hello_fn] 时,它会调用你定义的 hello_fn 过程宏函数。

  2. 过程宏接收到 greet() 函数作为输入,它首先用 syn 库解析了输入的函数(通过 parse_macro_input!(item as ItemFn))。也就是说,它把原本的函数(一个空的 greet 函数)转成了可以被 Rust 进一步处理的结构体。

  3. 然后,它通过 quote! 宏生成了一个新的函数代码。这个生成的新函数名字就是你传入的 greet(通过 #name),但是这个函数的实现已经变了,不再是空的了,它返回 "Hello, world!"

  4. 最后,生成的代码(新的 greet 函数)被返回给编译器,替换原来的空函数代码。

_attr: TokenStream 与 item: TokenStream

接下来我们用一个更复杂的例子来解释_attr: TokenStreamitem: TokenStream

1. item: TokenStream

item 是宏作用的目标代码,这部分代码可以是函数、结构体、枚举等。例如:

#[my_macro]
fn some_function() {println!("Hello, world!");
}

在这个例子中,item 就是函数 some_function() 的代码。你可以解析 item,修改它,或者在代码中做一些转换。

2. _attr: TokenStream

_attr 是宏的属性参数(即 #[my_macro(...)] 中的内容)。这部分通常是用户提供的配置,像是标志、字符串、数字或其他值。例如:

#[my_macro("hello")]
fn some_function() {println!("Hello, world!");
}

在这个例子中,_attr 就是 "hello" 字符串。

现在假设我们要实现一个属性宏,它接收一个字符串属性并打印该字符串,然后将目标函数的功能替换为返回该字符串。

use proc_macro::TokenStream;
use syn::{parse_macro_input, LitStr, ItemFn};
use quote::quote;#[proc_macro_attribute]
pub fn print_hello(_attr: TokenStream, item: TokenStream) -> TokenStream {// 解析 _attr(属性参数),这里假设我们传入的是一个字符串let attr_input = parse_macro_input!(_attr as LitStr);  // LitStr 是字符串字面量let greeting = attr_input.value();  // 获取字符串的值// 解析 item(目标代码),这里我们假设目标代码是一个函数let input_fn = parse_macro_input!(item as ItemFn);let fn_name = &input_fn.sig.ident;  // 获取目标函数的名称// 创建新的代码:新的函数实现,打印字符串,并返回这个字符串let expanded = quote! {pub fn #fn_name() -> &'static str {println!("{}", #greeting);#greeting}};// 返回生成的代码TokenStream::from(expanded)
}
解释:
  • _attr: TokenStream:我们使用 parse_macro_input! 把传入的 _attr 转换成一个 LitStr,即字符串字面量类型。在这个宏中,我们假设用户传入的是一个字符串,例如 #[print_hello("Hello, World!")]。通过 attr_input.value() 获取到字符串的实际值。

  • item: TokenStream:我们使用 parse_macro_input! 将目标代码(item)转换成 ItemFn,即目标函数的 AST 结构。在这里,我们提取了函数的名称(fn_name),以便在生成代码时保持一致。

这段代码使用了 quote! 宏来生成 Rust 代码片段。quote!quote 库提供的一个宏,用于在 Rust 中进行代码生成,它将 Rust 代码转化为 TokenStream,可以用于宏生成、代码插入、以及模板代码的生成。

let expanded = quote! {pub fn #fn_name() -> &'static str {println!("{}", #greeting);#greeting}
};
  1. quote! { ... }:

    • quote!quote 库提供的宏,它将其内部的代码块转换为 TokenStream,这可以用于后续的代码生成或宏扩展。
    • 你可以把 quote! 看作是一个 Rust 代码模板,允许在其中插入动态内容。
  2. #fn_name#greeting:

    • quote! 宏内部,使用 # 符号可以将 Rust 代码中定义的变量、表达式或者其他值插入到代码模板中。这样做可以在模板中动态替换变量。
    • #fn_name 是一个变量,它会被替换成你传递给 quote! 宏的具体函数名。比如,如果 fn_name 是一个字符串 "hello", 那么 #fn_name 会被替换成 hello
    • #greeting 是另一个变量,它会被替换成你传递的具体值,即 #[my_macro(...)] 中的内容。
  3. 生成的代码:

    • 该模板生成一个名为 fn_name 的函数,它返回一个 'static str 类型的字符串,并且在执行时打印一个 greeting 的值。
    • pub fn #fn_name() -> &'static str { ... } 生成一个公开的函数定义,其函数名是 #fn_name,返回类型是 'static str,即静态字符串。
    • println!("{}", #greeting); 生成了一条打印语句,用于输出 greeting 的内容。
    • 最后,#greeting 表示函数的返回值,它返回的是传入的 greeting 字符串。
使用:

假设我们在主程序中使用这个宏:

use my_proc_macro::print_hello;#[print_hello("Hello, world!")]
fn greet() {}fn main() {println!("{}", greet());  // 应该打印 "Hello, world!" 并返回该字符串
}
输出:
Hello, world!
Hello, world!

相关文章:

CTF-web: Rust 的过程宏

Rust 的过程宏(Procedural Macros)是一种强大的元编程工具,允许你在编译时对代码进行操作和生成。与属性宏和派生宏不同,过程宏可以接收并处理任意 Rust 代码,生成新的代码片段。这里有一个简单的例子来说明 Rust 的过…...

小程序Three Dof识别 实现景区AR体验

代码工程 GitCode - 全球开发者的开源社区,开源代码托管平台 dof...

Windows 11 下正确安装 Docker Desktop 到 D 盘的完整教程

文章目录 Windows 11 在 D 盘正确安装 Docker Desktop 的完整教程**前言****准备工作****1. 手动创建 Docker 相关目录**(⚠️ **这一步非常重要**,否则会报错)**2. 下载 Docker Desktop 安装程序****3. 使用管理员权限打开终端** **安装 Doc…...

锐评当前主流的各大编程语言

喵~ 让本喵来爪爪锐评一波编程语言吧!(ฅ•ω•ฅ)✧ 🐍 Python “简单到能拿爪子写代码喵~” 但缩进强迫症克星!写起来像在撸猫,跑起来…有时候像猫主子突然打翻水杯(GIL锁警告)~ 库多到能埋人&#xff0…...

2.数据结构:1.Tire 字符串统计

1.Tire 字符串统计 #include<algorithm> #include<cstring> #include<iostream>using namespace std;const int N100010; int son[N][26];//至多 N 层&#xff0c;每一层至多 26 个节点&#xff08;字母&#xff09; int cnt[N];//字符串至多 N 个&#xff…...

2020 年英语(一)考研真题 笔记(更新中)

Section I Use of English&#xff08;完型填空&#xff09; 原题 Directions&#xff1a;Read the following text. Choose the best word (s) for each numbered blank and mark A, B, C or D on the ANSWER SHEET. (10 points) Even if families are less likely to si…...

YOLO11改进加入ResNet网络

文章目录 1.改进目的2.demo引入2.1代码2.2 结果展示2.3 BottleNeck详解 1.改进目的 原始YOLO11模型训练好以后&#xff0c;检测结果mAP结果很低&#xff0c;视频检测结果很差&#xff0c;于是想到改进网络&#xff0c;这里介绍改进主干网络。 2.demo引入 2.1代码 # File: 2…...

硬编码(三)经典变长指令一

我们在前两节的硬编码中学习了定长指令&#xff0c;接下来学习变长指令 对于定长指令&#xff0c;我们通过opcode便可知该指令的长度&#xff0c;但是对于变长指令却是不可知的。变长指令长度由opcode&#xff0c;ModR/M&#xff0c;SIB共同决定。变长指令通常在需要操作内存的…...

在线VS离线TTS(语音合成芯片)有哪些优势-AIOT智能语音产品方案

离线 TTS 存在语音质量欠佳、音色选择有限、语言支持单一更新困难、占用资源多、适应性差、难以个性化定制等痛点 01更新维护困难 由于是离线模式&#xff0c;难以及时获取最新的语音数据和算法更新&#xff0c;无法得到持续改进。 02占用本地资源 需要在设备本地存储较大的…...

【Spring Boot】掌握 Spring 事务:隔离级别与传播机制解读与应用

前言 ???本期讲解关于spring 事务传播机制介绍~~~ ??感兴趣的小伙伴看一看小编主页&#xff1a;-CSDN博客 ?? 你的点赞就是小编不断更新的最大动力 ??那么废话不多说直接开整吧~~ 目录 ???1.事务的隔离级别 ??1.1MySQL事务隔离级别 ??1.2Spring事务隔离…...

PySide(PyQT)重新定义contextMenuEvent()实现鼠标右键弹出菜单

在 PySide中&#xff0c;contextMenuEvent() 是 QWidget 类&#xff08;以及继承自它的所有子类&#xff09;的一个事件处理方法&#xff0c;主要用于处理上下文菜单事件&#xff0c;也就是当用户在控件上右键点击时触发的事件。 • 通过重新定义contextMenuEvent()来实现自定…...

Redis 持久化方式:RDB(Redis Database)和 AOF(Append Only File)

本部分内容是关于博主在学习 Redis 时关于持久化部分的记录&#xff0c;介绍了 RDB 和 AOF 两种持久化方式&#xff0c;详细介绍了持久化的原理、配置、使用方式、优缺点和使用场景。并对两种持久化方式做了对比。文章最后介绍了 Redis 持久化的意义并与其他常见的缓存技术做了…...

数据库测试

TPCH 22条SQL语句分析 - xibuhaohao - 博客园 TPCH模型规范、测试说明及22条语句 - zhjh256 - 博客园 TPC-DS 性能比较&#xff1a;TiDB 与 Impala-PingCAP | 平凯星辰 揭秘Oracle TPC-H性能优化&#xff1a;如何提升数据库查询速度&#xff0c;揭秘实战技巧与挑战 引言 T…...

< 自用文儿 > Gobuster 暴力扫描工具与 SecLists 安全测试词表集合

Ethice 道德问题 GFW 的保护下&#xff0c;很多的设备操作系统是停留在更老的版本&#xff0c;应用软件也是&#xff0c;因此很多的漏洞没有被修复。通讯没有使用加密&#xff0c;例如网页没有使用 HTTPS 网站很多。几乎是半裸的在网络上等着被食。 不做恶是下限。 环境&…...

上海市计算机学会竞赛平台2024年4月月赛丙组排序分数

排序分数 内存限制: 256 Mb时间限制: 1000 ms 题目描述 给定正整数 nn&#xff0c;请按从小到大的顺序输出所有大于00 且小于 11 的&#xff0c;分母不超过 nn 的最简分数&#xff0c;例如 n5n5 时&#xff0c;输出&#xff1a; 15, 14, 13, 25, 12, 35, 23, 34, 45…...

【大语言模型笔记进阶一步】提示语设计学习笔记,跳出框架思维,自己构建提示词

一、大语言模型应用场景 1. 文本生成 文本创作&#xff1a; 诗歌故事&#xff0c;剧本&#xff0c;推文帖子 摘要与改写&#xff1a; 长文本摘要与简化&#xff0c;多语言翻译与本地化 结构化生成&#xff1a; 表格&#xff0c;根据需求生成代码片段&#xff0c;API文档生成…...

软件工程应试复习(考试折磨版)

针对学校软件工程考试&#xff0c;参考教材《软件工程导论&#xff08;第6版&#xff09;》1-8章 学习的艺术&#xff1a;不断地尝试&#xff0c;我一定会找到高效用的方法&#xff0c;让学习变成一门艺术&#xff0c;从应试备考中解救出我的时间同胞们。 好嘞&#xff01;既然…...

关于网页地图的坐标系

EPSG:4326地理坐标系 和 EPSG:3857Web 墨卡托投影 EPSG:4326 定义&#xff1a;EPSG:4326 是基于 WGS84 椭球的地理坐标系&#xff0c;使用经度&#xff08;Longitude&#xff09;和纬度&#xff08;Latitude&#xff09;表示地球上的位置。特点&#xff1a; 经度范围为 -180 …...

环境会影响你的决策:K近邻算法(KNN)

环境会影响你的决策&#xff1a;K近邻算法&#xff08;KNN) 1. 核心思想与流程 KNN是一种基于局部相似性的分类算法&#xff0c;核心思想是“近朱者赤”&#xff1a;待测样本的类别由其最近的k个邻居的多数类别决定。 关键步骤&#xff1a; 定义空间与距离&#xff1a;通常采…...

华为云之使用鲲鹏弹性云服务器部署Node.js环境【玩转华为云】

华为云之使用鲲鹏弹性云服务器部署Node.js环境【玩转华为云】 一、本次实践介绍1.1 实践环境简介1.3 本次实践完成目标 二、 相关服务介绍2.1 华为云ECS云服务器介绍2.2 Node.js介绍 三、环境准备工作3.1 预置实验环境3.2 查看预置环境信息 四、登录华为云4.1 登录华为云4.2 查…...

Vue 3 路由管理实战:构建多页面博客导航 - 掌握 Vue Router 实现 SPA 页面跳转

引言 欢迎再次回到 Vue 3 + 现代前端工程化 系列技术博客! 在昨天的第三篇博客中,我们深入探索了 Vue 3 响应式系统的进阶应用,通过构建简易购物车应用,熟练掌握了 watch 监听器和 computed 计算属性的运用。 今天,我们将开启 Vue 3 工程化实践的全新篇章,聚焦于构建单页…...

C语言整体梳理-基础篇-结构体

结构体详解 1.1结构体是什么&#xff1f; 结构体是一些值的集合&#xff0c;这些值成为成员变量&#xff0c;结构体的每个成员可以是不同类型的变量。 数组是相同类型的元素组成的集合&#xff0c;结构体可以是不同类型元素组成的集合。 1.2结构体的声明 1.2.1常规声明 s…...

MacBook 终端中使用 vim命令

在 MacBook 终端中使用 vim 编辑器时&#xff0c;以下是一些常用命令和操作指南&#xff1a; 1. 基本操作 启动 vim vim 文件名 # 打开或创建文件退出 vim 保存并退出&#xff1a; 按 Esc&#xff0c;然后输入 :wq&#xff0c;按 Enter。 不保存退出&#xff1a; 按 Esc&am…...

【 实战案例篇三】【某金融信息系统项目管理案例分析】

大家好,今天咱们来聊聊金融行业的信息系统项目管理。这个话题听起来可能有点专业,但别担心,我会尽量用大白话给大家讲清楚。金融行业的信息系统项目管理,说白了就是如何高效地管理那些复杂的IT项目,确保它们按时、按预算、按质量完成。咱们今天不仅会聊到一些理论,还会通…...

springboot、deepseek4j、bge-m3和milvus

1、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"http://maven.apache.org/POM/4.0.0 …...

会话与会话管理:Cookie与Session的深度解析

一、什么是会话&#xff1f; 二、Cookie&#xff1a;客户端存储技术 1. Cookie的工作原理 2、在后端设置cookie 3、在前端设置cookie 三、浏览器开启了cookie禁用怎么办&#xff1f; 一、什么是会话&#xff1f; 会话&#xff08;Session&#xff09;是指一个用户与服务器之间…...

etcd部署硬件资源推荐

etcd部署硬件资源推荐 原文&#xff1a;https://etcd.io/docs/v3.5/op-guide/hardware/ etcd 通常在开发或测试环境中运行良好&#xff0c;即使资源有限&#xff1b;在笔记本电脑或廉价云服务器上开发时&#xff0c;使用 etcd 也很常见。然而&#xff0c;在生产环境中运行 etcd…...

MAVlink链路环境搭建并解决“ModuleNotFoundError: No module named ‘xxx’”问题

MAVlink链路常用于云台相机与飞控以及地面站之间的数据传输&#xff0c;搭建MAVlink链路环境需要安装Python、Future、MAVLink、pymavlink四样工具用于生成mavlink代码。 Python 直接从官网下载默认安装即可https://www.python.org/downloads/ 在电脑命令行进行安装验证&#x…...

ROS2软件调用架构和机制解析:Publisher创建

术语 DDS (Data Distribution Service): 用于实时系统的数据分发服务标准&#xff0c;是ROS 2底层通信的基础RMW (ROS Middleware): ROS中间件接口&#xff0c;提供与具体DDS实现无关的抽象APIQoS (Quality of Service): 服务质量策略&#xff0c;控制通信的可靠性、历史记录、…...

Android -- 使用Sharepreference保存List储存失败,原因是包含Bitmap,drawable等类型数据

1.报错信息如下&#xff1a; class android.content.res.ColorStateList declares multiple JSON fields named mChangingConfigurations 2.Bean类属性如下&#xff1a; data class AppInfoBean( val appName: String?, val appIcon: Drawable, val appPackage: String?,…...