过程宏:派生宏与函数宏
深入了解过程宏,包括自定义 `#[derive]` 宏和函数式宏,用于生成更复杂的代码。
过程宏:派生宏与函数宏 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 2 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 3 节课。
过程宏简介
欢迎来到过程宏的世界!与声明式宏(macro_rules!)不同,过程宏类似于直接处理 Rust 代码本身的函数。
它们接收一个 TokenStream 作为输入,处理后返回另一个 TokenStream。这使得它们能够生成复杂得多的代码。
过程宏的类型
Rust 提供三种主要的过程宏:
- 类函数宏:它们看起来和行为都像普通的函数调用,例如
my_macro!(...)。 - 派生宏:您可以使用
#[derive(MyTrait)],为结构体和枚举自动实现特征。 - 属性宏:您可以定义自定义属性,并将其应用于项目,例如
#[route("/path")]。
`proc-macro` crate
过程宏必须位于专用的 crate 类型中。在您的 Cargo.toml 中,可以这样声明:
这会告诉 Cargo,该 crate 包含在编译时转换代码的宏。
[package]
name = "my_macros"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true类函数宏详解
类函数宏通过函数上的 #[proc_macro] 属性定义。该函数接收一个 proc_macro::TokenStream,并返回一个 proc_macro::TokenStream。
它们适合用于自定义领域专用语言(DSL),或用于处理 macro_rules! 无法完成的复杂代码重复。
use proc_macro::TokenStream;
#[proc_macro]
pub fn my_function_macro(input: TokenStream) -> TokenStream {
// Logic to transform input TokenStream
// ...
input // For example, return the input unchanged
}类函数宏示例
下面是一个概念性示例。宏 `greet_name!` 会生成一条打印语句。实际实现需要解析输入的 TokenStream,以提取名称。
虽然宏的定义很复杂,但使用起来简单而强大:
/* In 'my_macros/src/lib.rs' */
use proc_macro::TokenStream;
#[proc_macro]
pub fn greet_name(input: TokenStream) -> TokenStream {
// Imagine parsing 'input' to get a name like 'World'
// and generating: println!("Hello, World!");
"println!(\"Hello, {}!\", \"CoddyKit\");".parse().unwrap()
}
/* In 'my_app/src/main.rs' */
use my_macros::greet_name;
fn main() {
greet_name!("CoddyKit");
}理解 `#[derive]` 宏
派生宏是最常见的类型。它们允许您为结构体或枚举自动实现特征。
当您编写 #[derive(Debug)] 时,宏会在编译时运行,生成必要的代码,使您的类型实现 Debug 特征。
实现 `#[derive]`(概念)
自定义派生宏通常使用 syn(用于将 Rust 代码解析为 AST)和 quote(用于根据 AST 生成 Rust 代码)等库来构建。
您需要定义一个带有 #[proc_macro_derive(MyTrait)] 注解的函数。该函数接收表示结构体或枚举的 TokenStream,并返回生成的特征实现。
/* In 'my_macros/src/lib.rs' */
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
use quote::quote;
#[proc_macro_derive(MyDebug)] // MyDebug is the trait name
pub fn my_debug_derive(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let name = &ast.ident;
let expanded = quote! {
impl std::fmt::Debug for #name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!(#name)).finish()
}
}
};
expanded.into()
}使用 `#[derive(Debug)]` 的示例
让我们来看一个您很可能已经使用过的派生宏实际示例:Debug。通过向 Point 结构体添加 #[derive(Debug)],Rust 会自动实现 Debug 特征,让我们能够以清晰易读的方式打印其内容。
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 10, y: 20 };
println!("My point is: {:?}", p);
}属性宏:`#[my_attribute]`
属性宏与派生宏类似,但可以应用于任何项目(函数、结构体、模块等)。它们会接收所附加的项目,以及括号中的任意参数作为输入。
常见用途包括 Web 框架路由(例如 #[get("/users")])或测试设置(例如 #[test])。
/* In 'my_macros/src/lib.rs' */
use proc_macro::TokenStream;
#[proc_macro_attribute]
pub fn log_calls(attr: TokenStream, item: TokenStream) -> TokenStream {
// Imagine adding println! statements to 'item'
item
}
/* In 'my_app/src/main.rs' */
use my_macros::log_calls;
#[log_calls("entry_point")]
fn my_function() {
println!("Inside my_function");
}
fn main() {
my_function();
}何时使用哪种宏
- 类函数宏:用于自定义小型语言,或转换任意代码块的复杂操作。
- 派生宏:用于为结构体和枚举自动实现特征。
- 属性宏:根据自定义属性,修改特定项目(函数、结构体)周围的代码,或生成相关代码。
每种类型都有独特的用途,可以扩展 Rust 的语法和功能。
快速检查:宏的类型
关于过程宏,以下哪些说法是 TRUE?
回顾:代码生成的力量
您已经探索了过程宏的高级世界!
- 我们学习了用于自定义语法的类函数宏。
- 我们了解了
#[derive]宏如何自动实现特征。 - 我们简要了解了用于项目级代码生成的属性宏。
过程宏是减少样板代码和扩展 Rust 功能的强大工具,让您能够编写生成代码的代码!
常见问题解答
「过程宏:派生宏与函数宏」课时是免费的吗?
是的 — 「过程宏:派生宏与函数宏」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 3 节课。
「过程宏:派生宏与函数宏」这节课中我会学到什么?
深入了解过程宏,包括自定义 `#[derive]` 宏和函数式宏,用于生成更复杂的代码。 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Learn Rust Coding 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 3 节。
「过程宏:派生宏与函数宏」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Learn Rust Coding 课中编写并运行代码吗?
能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 声明式宏(`macro_rules!`)
- 过程宏:派生宏与函数宏
- 与不安全 Rust 交互