Rust 宏笔记

本贴最后更新于 1883 天前,其中的信息可能已经沧海桑田


这篇文章说的是?

Rust 的宏。


宏按照来源分类:

声明宏(Declarative Macro)和过程宏(Procedural Macro)。前者指的是用某种语法直接声明出的宏。后者是对应直接生成抽象语法树的过程的宏。

直觉上过程宏更隐式,更全能;声明宏更可读,更直接。


如何定义声明宏?

现在用 macro_rules!。以后可能还有别的办法。


如何定义过程宏?

以后再说。


宏按照使用方式分类:

属性宏:给声明添加属性的宏,例如 #[derive(Debug)]#[test]

调用宏:像函数一样的宏,例如 println!


来源分类和使用方式分类之间的关系如何?

目前的声明宏都是用 macro_rules! 声明出的,它声明出的一定是调用宏。过程宏可以产生属性宏,也可以产生调用宏。

也就是说,属性宏都是过程宏,调用宏可能是声明宏或者过程宏。


println! 宏大概是什么样子?

macro_rules! println { () => (println!("\n")); ($fmt: expr) => (print!(concat!($fmt, "\n"))); ($fmt: expr, $($(arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); }

这个宏有几部分?

有三个部分,输入分别是 ()($fmt:expr)($fmt: expr, $($args:tt)*),依次扩展成 => 后,圆括号内部的部分。每个部分是一条规则,每条规则以 ; 结尾。


=> 后的圆括号是必须的吗?

不能省略,但是可以换成 {} 或者 []


$fmt: expr 是什么?

$fmt 是对宏参数的捕获,类似于函数的参数。expr 表示这个捕获的类型是表达式,也就是会它会生成具体的值。具体到此处,它代表生成 println! 的格式字符串的表达式。


捕获有什么用?

宏的替换结果里可以用 $fmt 代表要替换这个捕获。例如 println!("Hello") 中,$fmt: expr 捕获了 "Hello",所以 print!(concat!($fmt, "\n)) 中的 $fmt 会被替换为 "Hello",所以展开成 print!(concat!("Hello", "\n"))


展开的宏中如果还有宏,还会继续展开吗?

会,上面 println! 展开之后的内容中有 print!concat!,它们都会再次展开。这是理所当然的行为,这个问题只是为了让 Rust 的宏跟 C++ 的宏划清界限。


都有什么捕获类型?

类型 意义
item 语言项,模块、定义、声明等
block 代码块,花括号限定的代码
stmt 语句,分号结尾的代码
expr 表达式,会生成具体的值
pat 模式
ty 类型
ident 标识符
path 路径,指从 crate 到 mod 的定位
meta 元信息
tt TokenTree 的缩写,词条树
vis 可见性,例如 pub
lifetime 生命周期参数

$($arg:tt)* 是什么意思?

单独看 $arg:tt 表示匹配一个词条树的捕获,在外面套上 $()* 表示匹配若干次词条树。


$($arg)* 是什么意思?

单看 $arg,表示在宏里替换捕获 $arg,外面套上 $()* 表示使用所有匹配的捕获。这个用法跟它的捕获语法是对应的。


$($arg:tt)* 能匹配什么?

println! 在第一个参数之后的所有东西。这个宏不止可以传递像函数一样的参数,还可以像 Python 那样传递命名参数,例如:

println!("Hello, {name}", name="Luna");

这样的参数 $($arg:tt)* 也能捕获到。


如果想只捕获(不定个数个)函数参数应该如何做?

$($arg: expr),* 或者 $($arg: expr,)*


这两个有什么不同?

后者也匹配逗号结尾的参数列表。

Rust 的函数参数列表最后可以添加逗号,也可以不加。如果想让宏表现的尽量接近函数,应该两种情况都处理。


举个例子。

macro_rules! hash_map { ($($key:expr => $value:expr),*) => {{ let mut map = ::std::collections::HashMap::new(); $( map.insert($key, $value); )* map }}; ($($key:expr => $value:expr),*) => (hash_map! ($($key => $value),*)); }

怎么使用?

let map = hash_map! (1 => "one", 2 => "two", 3 => "three");

如何获取宏可变参数的长度?

没有直接的办法,但可以想些技巧。

macro_rules! unit { ($($x:tt)*) => (()); } macro_rules! count { ($($x:expr),*) => (<[()]>::len(&[$(unit!($x)),*])); }

它是如何工作的?

unit! 接受任意什么东西,返回一个 unit(())。count! 宏把参数填给 unit!,构造了一个 unit 数组,数组长度就是参数的个数。

好处是,unit 不占空间,unit 的数组也是。


count! 宏需要 unit! 宏才能工作,但 unit! 宏本身没什么用,能不能把 unit! 变成私有的?

也没有直接的办法。基本上的技巧是换成一个比较难直接用到的规则:

macro_rules! count { (@unit $($x:tt)*) => (()); ($($x:expr),*) => (<[()]>::len(&[$(count!(@unit $x)),*])); }

要是有人非要写 count!(@unit ...),也没法阻止。但那个奇奇怪怪的 @ 已经暗示了这是一个内部实现,这就足够了。


#[macro_export] 有什么作用?

标记一个宏可以在其他包中使用。也就是说,默认情况下,宏不能在定义的包外使用。


#[macro_use] 有什么用?

在 Rust 2015 中,在外部包的声明语前,用它标记要导入另一个包的宏。另外,它还标记一个 mod 的宏可以在外面使用。


宏重名会怎样?

后导入的会覆盖先前导入的,不会发生错误。


$crate 有什么用?

它看起来像一个捕获,但不是在宏参数列表里捕获的。它会扩展成当前包的名字。需要这个捕获的原因是,当前包无法决定此包被其他包导入的时候,使用的是什么名字。所以如果要使用当前包的函数或者别的东西,就需要从 $crate 开始写路径。


完。

  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    59 引用 • 22 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • Shell

    Shell 脚本与 Windows/Dos 下的批处理相似,也就是用各类命令预先放入到一个文件中,方便一次性执行的一个程序文件,主要是方便管理员进行设置或者管理用的。但是它比 Windows 下的批处理更强大,比用其他编程程序编辑的程序效率更高,因为它使用了 Linux/Unix 下的命令。

    125 引用 • 74 回帖
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    172 引用 • 1546 回帖
  • Logseq

    Logseq 是一个隐私优先、开源的知识库工具。

    Logseq is a joyful, open-source outliner that works on top of local plain-text Markdown and Org-mode files. Use it to write, organize and share your thoughts, keep your to-do list, and build your own digital garden.

    7 引用 • 69 回帖 • 4 关注
  • 服务器

    服务器,也称伺服器,是提供计算服务的设备。由于服务器需要响应服务请求,并进行处理,因此一般来说服务器应具备承担服务并且保障服务的能力。

    125 引用 • 585 回帖
  • Notion

    Notion - The all-in-one workspace for your notes, tasks, wikis, and databases.

    10 引用 • 77 回帖
  • Log4j

    Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。

    20 引用 • 18 回帖 • 41 关注
  • Laravel

    Laravel 是一套简洁、优雅的 PHP Web 开发框架。它采用 MVC 设计,是一款崇尚开发效率的全栈框架。

    19 引用 • 23 回帖 • 744 关注
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    211 引用 • 358 回帖
  • 负能量

    上帝为你关上了一扇门,然后就去睡觉了....努力不一定能成功,但不努力一定很轻松 (° ー °〃)

    89 引用 • 1251 回帖 • 390 关注
  • PWA

    PWA(Progressive Web App)是 Google 在 2015 年提出、2016 年 6 月开始推广的项目。它结合了一系列现代 Web 技术,在网页应用中实现和原生应用相近的用户体验。

    14 引用 • 69 回帖 • 186 关注
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1444 引用 • 10083 回帖 • 499 关注
  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    89 引用 • 150 回帖
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖
  • 周末

    星期六到星期天晚,实行五天工作制后,指每周的最后两天。再过几年可能就是三天了。

    14 引用 • 297 回帖 • 2 关注
  • CloudFoundry

    Cloud Foundry 是 VMware 推出的业界第一个开源 PaaS 云平台,它支持多种框架、语言、运行时环境、云平台及应用服务,使开发人员能够在几秒钟内进行应用程序的部署和扩展,无需担心任何基础架构的问题。

    4 引用 • 16 回帖 • 201 关注
  • Unity

    Unity 是由 Unity Technologies 开发的一个让开发者可以轻松创建诸如 2D、3D 多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

    25 引用 • 7 回帖 • 117 关注
  • SQLServer

    SQL Server 是由 [微软] 开发和推广的关系数据库管理系统(DBMS),它最初是由 微软、Sybase 和 Ashton-Tate 三家公司共同开发的,并于 1988 年推出了第一个 OS/2 版本。

    21 引用 • 31 回帖 • 1 关注
  • RabbitMQ

    RabbitMQ 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种语言客户端,如:Python、Ruby、.NET、Java、C、PHP、ActionScript 等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    49 引用 • 60 回帖 • 349 关注
  • Sublime

    Sublime Text 是一款可以用来写代码、写文章的文本编辑器。支持代码高亮、自动完成,还支持通过插件进行扩展。

    10 引用 • 5 回帖
  • Telegram

    Telegram 是一个非盈利性、基于云端的即时消息服务。它提供了支持各大操作系统平台的开源的客户端,也提供了很多强大的 APIs 给开发者创建自己的客户端和机器人。

    5 引用 • 35 回帖
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖 • 1 关注
  • Hadoop

    Hadoop 是由 Apache 基金会所开发的一个分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。

    93 引用 • 122 回帖 • 620 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 65 关注
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    2 引用 • 14 回帖 • 4 关注
  • SVN

    SVN 是 Subversion 的简称,是一个开放源代码的版本控制系统,相较于 RCS、CVS,它采用了分支管理系统,它的设计目标就是取代 CVS。

    29 引用 • 98 回帖 • 697 关注
  • Flutter

    Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作,它正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。

    39 引用 • 92 回帖 • 11 关注
  • 叶归
    14 引用 • 62 回帖 • 26 关注