使用 cargo-expand 命令查看 rust 宏的展开代码

1,769 阅读1分钟

文档背景

cargo version
cargo 1.75.0 (1d8b05cdd 2023-11-20)
# 当前最新的cargo/rust 版本

前言

在做 rust 开发的时候, 我们可能很轻易地写下了如下的代码 println!("foo baz") , 但是 println! 是一个宏,rustc 在编译的时候会对这些代码做一个预处理。特别是使用一些非常特殊的宏的使用,特别希望看到展开之后的代码。比如在做 tokio 任务配置的时候

cargo-expand 宏展开工具

  1. 安装 cargo-expand
cargo  install cargo-expand
  1. 创建 cargo-expand-demo 并进入 cargo-expand-demo 项目

    cargo new cargo-expand-demo
    
  2. 添加 指定版本的tokio

    # --dry-run cargo 命令不会修改 Cargo.toml 文件
    cargo add tokio@1.36.0 --no-default-features --features full 
    cargo check
    
  3. 修改 main.rs _

    // 使用 tokio 运行时
    #[tokio::main]
    async fn main() {
        // 使用 println! 宏向控制台输出 Hello, World!
        println!("Hello, world!");
    }
    
  4. 使用 cargo-expand 展开 main.rs 文件得到如下输出

    #![feature(prelude_import)]
    #[prelude_import]
    use std::prelude::rust_2021::*;
    #[macro_use]
    extern crate std;
    fn main() {
        let body = async {
            {
                ::std::io::_print(format_args!("Hello, world!\n"));
            };
        };
        #[allow(clippy::expect_used, clippy::diverging_sub_expression)]
        {
            return tokio::runtime::Builder::new_multi_thread()
                .enable_all()
                .build()
                .expect("Failed building the Runtime")
                .block_on(body);
        }
    }
    
  5. main.rs 与展开的代码对比

println!("Hello, world!"); 被替换成 ::std::io::_print(format_args!("Hello, world!\n"));

// 这里是纯分析 main 函数
#[tokio::main]
async fn main(){
    // 函数体
}
fn main() {
    let body = async {
        
    };

    {
        return tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .expect("Failed building the Runtime")
            .block_on(body);
    }
}

通过分析展开之后的代码逻辑还是非常直观的。在面对非常复杂的宏的时候,cargo-expand 还是非常有用的。

参考

  1. cargo 外部命令
  2. cargo add 命令