文档背景
cargo version cargo 1.75.0 (1d8b05cdd 2023-11-20) # 当前最新的cargo/rust 版本
前言
在做 rust 开发的时候, 我们可能很轻易地写下了如下的代码 println!("foo baz") , 但是 println! 是一个宏,rustc 在编译的时候会对这些代码做一个预处理。特别是使用一些非常特殊的宏的使用,特别希望看到展开之后的代码。比如在做 tokio 任务配置的时候
cargo-expand 宏展开工具
- 安装 cargo-expand
cargo install cargo-expand
-
创建 cargo-expand-demo 并进入 cargo-expand-demo 项目
cargo new cargo-expand-demo -
添加 指定版本的
tokio# --dry-run cargo 命令不会修改 Cargo.toml 文件 cargo add tokio@1.36.0 --no-default-features --features full cargo check -
修改
main.rs_// 使用 tokio 运行时 #[tokio::main] async fn main() { // 使用 println! 宏向控制台输出 Hello, World! println!("Hello, world!"); } -
使用
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); } } -
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 还是非常有用的。