过程宏内幕解析「二」

1,373 阅读2分钟

拓展新的语法

我们来看看 yew 中的过程宏 html!,它可以帮助开发者在Rust中编写前端页面(我们将在后面看程序性宏)。下面是一个关于如何调用这个宏的例子:

use yew::html;

html! {
      <div>
         <div class="panel">
             { "Hello, World!" }
         </div>
      </div>
}

宏调用看起来一点也不像Rust,是不是?但是 html! 将其调用的代码解析为类似于 HTML 的语言,并生成了一个叫做 虚拟DOM 的层级结构。由此产生的扩展代码是纯粹的Rust,而且它是可以被rustc编译:

VTag::new(
   "div",
   vec![VTag::new("div", ...)],
);

这就是宏如何将另一种语言嵌入到Rust中的例子。

⚠️ 宏主体中的空白被消除了,所以没有办法写出,比如说,注入Python这样的语言的宏。

下面是更多关于宏如何有助创建自定义语法的例子:

  • Collection:如标准库中的vec。
  • 文本格式化:由 println/format 这样的宏呈现(println! 是一个声明宏,它扩展到rustc中包含的过程宏 format_args_nl)。

请注意,禁止的符号在过程宏中不能使用。总而言之,程序宏中只能包含Rust中已经允许的符号。

减少模版代码

为了说明这一点,我们使用一个 struct 例子。通常情况,struct 有许多 trait 需要实现:

struct Foo { x: i32, y: i32 }

impl Copy for Foo { ... }
impl Clone for Foo { ... }
impl Ord for Foo { ... }
impl PartialOrd for Foo { ... }
impl Eq for Foo { ... }
impl PartialEq for Foo { ... }
impl Debug for Foo { ... }
impl Hash for Foo { ... }

这里派上用场的是 derive,它是一个过程宏。上述 trait 可以使用rustc的 derive宏(#[derive]) 来重写。

#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash, Default)]
struct Foo { x: i32, y: i32 }

每一次 derive 都会在原始结构的基础上产生一个特定的 impl block

过程宏

从本质上讲,过程宏 是一个在编译时执行的Rust函数。这类函数属于一个特殊的 crate,并标有 proc-macro 标记。详细的代码在 Cargo.toml 中:

[package]
name = "my-proc-macro"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true

TODO!!!

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情