bigywater.tech
Rust · 81

Attribute Macro

属性宏比 derive 更强大也更危险:它能改写甚至删除被修饰的条目。这篇写三个最小的属性宏,让编译器展开出来,再验证参数解析、保留原条目、解析错误和用错位置的各种报错。

2 个输入
属性宏收到的 token 流:attr 与 item
E0425
属性宏返回空时,被修饰函数消失后的错误
1
展开后的 add 里,原函数体被包进的闭包个数

包装函数体

实测 · 本机执行输出
  编译退出码 0 []
  运行:
    [timed] add 返回了(耗时数量级 微秒以内)
    add = 5
    [timed] slow 返回了(耗时数量级 毫秒)
    slow = done
  展开后的 add:
    fn add(a: i32, b: i32) -> i32 {
        let __t = std::time::Instant::now();
        let __r = (|| { a + b })();
        {
            ::std::io::_print(format_args!("[timed] {0} 返回了(耗时数量级 {1})\n",
                    "add",
                    if __t.elapsed().as_millis() >= 1 {
                        "毫秒"
                    } else { "微秒以内" }));
        };
        __r
    }

过程宏的阶段见过程宏的执行阶段。

带参数并保留原函数

实测 · 本机执行输出
  编译退出码 0
  运行:
    ("GET", "/users", "list_users")
    ("POST", "/users", "create_user")
    原函数仍可调用: users / created

参数错误

实测 · 本机执行输出
  error: expected `,`
   --> src/main.rs:3:1
    |
  3 | #[route("GET")] fn a() {}
    | ^^^^^^^^^^^^^^^
    |
    = note: this error originates in the attribute macro `route` (in Nightly builds, run with -Z macro-backtrace for more info)
  error: expected string literal
   --> src/main.rs:4:9
    |
  4 | #[route(GET, "/x")] fn b() {}
    |         ^^^
  error: could not compile `app` (bin "app") due to 2 previous errors

返回值替换原条目

实测 · 本机执行输出
  error[E0425]: cannot find function `gone` in this scope
     --> src/main.rs:4:28
      |
    4 | fn main() { println!("{}", gone()); }
      |                            ^^^^
      |

用错位置

实测 · 本机执行输出
  error: expected `fn`
   --> src/main.rs:3:10
    |
  3 | #[timed] struct NotFn;
    |          ^^^^^^
  error: could not compile `app` (bin "app") due to 1 previous error

derive 宏的写法见Derive 宏如何生成代码。

分步演示

属性宏
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。crates:syn 2、quote 1、proc-macro2 1
    • 没有验证:对 impl 块与 trait 的属性宏、async fn 的包装(闭包写法不适用于 async)、#[timed] 对带 self 方法的处理、属性宏之间的叠加顺序、tracing / tokio::main 等真实 crate 的做法
    京ICP备2026067502号-1京公网安备11010502063487号