Giao diện
Metaprogramming
"Macros là code viết code." - Rust Book
1. Declarative Macros (macro_rules!)
Pipeline biên dịch Macro
Source Code Mở rộng Macro Đã biên dịch
─────────── ───────────── ────────────
vec![1, 2, 3] ──────▶ { ──────▶ Binary
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
v
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Cú pháp Macro
rust
macro_rules! say_hello {
// Pattern => Mở rộng
() => {
println!("Xin chào!");
};
}
say_hello!(); // Mở rộng thành: println!("Xin chào!");1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Pattern Matching trong Macros
rust
macro_rules! create_function {
// Match identifier và type
($func_name:ident, $type:ty) => {
fn $func_name(x: $type) -> $type {
x * 2
}
};
}
create_function!(double_i32, i32);
create_function!(double_f64, f64);
// Mở rộng thành:
// fn double_i32(x: i32) -> i32 { x * 2 }
// fn double_f64(x: f64) -> f64 { x * 2 }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Fragment Specifiers
| Specifier | Khớp với | Ví dụ |
|---|---|---|
ident | Identifier | foo, Vec |
expr | Expression | 1 + 2, func() |
ty | Type | i32, Vec<String> |
pat | Pattern | Some(x), _ |
stmt | Statement | let x = 1; |
block | Block | { ... } |
item | Item | fn foo() {} |
meta | Meta item | cfg(test) |
tt | Token tree | Bất kỳ |
literal | Literal | 1, "hello" |
Patterns lặp
rust
macro_rules! vec_of_strings {
// $(...),* nghĩa là "không hoặc nhiều, phân cách bởi dấu phẩy"
($($x:expr),*) => {
{
let mut v = Vec::new();
$(
v.push($x.to_string());
)*
v
}
};
}
let v = vec_of_strings!["a", "b", "c"];
// Mở rộng thành:
// {
// let mut v = Vec::new();
// v.push("a".to_string());
// v.push("b".to_string());
// v.push("c".to_string());
// v
// }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Toán tử lặp
rust
// $(...)* - Không hoặc nhiều
// $(...)+ - Một hoặc nhiều
// $(...)? - Không hoặc một
macro_rules! match_all {
// Dấu phẩy cuối tùy chọn
($($x:expr),+ $(,)?) => {
// ...
};
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Macro Hygiene
rust
macro_rules! using_a {
($e:expr) => {
{
let a = 42; // 'a' này là hygienic
$e // Không thể truy cập 'a' của macro
}
}
}
let a = 10;
let result = using_a!(a * 2); // Dùng 'a' bên ngoài = 20, không phải 421
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Ví dụ thực tế: HashMap Literal
rust
macro_rules! hashmap {
($($key:expr => $value:expr),* $(,)?) => {
{
let mut map = std::collections::HashMap::new();
$(
map.insert($key, $value);
)*
map
}
};
}
let scores = hashmap! {
"Alice" => 100,
"Bob" => 85,
"Charlie" => 92,
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2. Procedural Macros
Các loại Procedural Macros
Các loại Procedural Macro
┌──────────────────────────────────────────────────────────┐
│ │
│ Kiểu Function Derive Attribute │
│ ───────────── ────── ───────── │
│ │
│ my_macro!(...) #[derive(MyTrait)] #[my_attr] │
│ fn foo() {} │
│ │
│ Input: TokenStream Input: Struct Input: Item │
│ Output: TokenStream Def only + Attribute │
│ Output: Impl │
│ TokenStream │
│ │
└──────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Thiết lập Project
toml
# Cargo.toml
[lib]
proc-macro = true
[dependencies]
quote = "1.0"
syn = { version = "2.0", features = ["full"] }
proc-macro2 = "1.0"1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Ví dụ Derive Macro
rust
// Trong proc-macro crate: my_derive/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(HelloWorld)]
pub fn hello_world_derive(input: TokenStream) -> TokenStream {
// Parse input tokens thành syntax tree
let input = parse_macro_input!(input as DeriveInput);
// Lấy tên của struct/enum
let name = input.ident;
// Tạo implementation
let expanded = quote! {
impl HelloWorld for #name {
fn hello() {
println!("Xin chào thế giới! Tôi là {}", stringify!(#name));
}
}
};
TokenStream::from(expanded)
}
// Sử dụng:
#[derive(HelloWorld)]
struct Pancakes;
fn main() {
Pancakes::hello(); // "Xin chào thế giới! Tôi là Pancakes"
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Cấu trúc AST (syn)
rust
// syn parse Rust code thành AST
// DeriveInput đại diện cho:
pub struct DeriveInput {
pub attrs: Vec<Attribute>, // #[...]
pub vis: Visibility, // pub, pub(crate), etc
pub ident: Ident, // Tên của type
pub generics: Generics, // <T, U>
pub data: Data, // struct fields hoặc enum variants
}
pub enum Data {
Struct(DataStruct),
Enum(DataEnum),
Union(DataUnion),
}
pub struct DataStruct {
pub fields: Fields, // Named, Unnamed, hoặc Unit
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Ví dụ Attribute Macro
rust
// Attribute macro: transform toàn bộ item
#[proc_macro_attribute]
pub fn log_calls(attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as syn::ItemFn);
let name = &input.sig.ident;
let body = &input.block;
let attrs = &input.attrs;
let vis = &input.vis;
let sig = &input.sig;
let expanded = quote! {
#(#attrs)*
#vis #sig {
println!("Đang vào: {}", stringify!(#name));
let result = #body;
println!("Đang ra: {}", stringify!(#name));
result
}
};
TokenStream::from(expanded)
}
// Sử dụng:
#[log_calls]
fn add(a: i32, b: i32) -> i32 {
a + b
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Ví dụ Function-like Macro
rust
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
let input = input.to_string();
// Parse SQL tại compile time
// Validate cú pháp, trích xuất parameters
// Tạo type-safe query struct
let expanded = quote! {
// Code được tạo ở đây
};
TokenStream::from(expanded)
}
// Sử dụng:
let query = sql!(SELECT * FROM users WHERE id = ?);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
3. Advanced Macro Patterns
Recursive Macros
rust
macro_rules! count_exprs {
() => { 0 };
($head:expr) => { 1 };
($head:expr, $($tail:expr),*) => {
1 + count_exprs!($($tail),*)
};
}
let n = count_exprs!(a, b, c, d); // 41
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
TT Muncher Pattern
rust
// Xử lý tokens từng cái một
macro_rules! mixed_up {
// Base case
() => {};
// Match và transform
(@ $e:expr, $($rest:tt)*) => {
println!("At: {}", $e);
mixed_up!($($rest)*);
};
(# $e:expr, $($rest:tt)*) => {
println!("Hash: {}", $e);
mixed_up!($($rest)*);
};
}
mixed_up!(@ 1, # 2, @ 3,);
// In ra:
// At: 1
// Hash: 2
// At: 3
))}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Internal Rules
rust
macro_rules! foo {
// Giao diện công khai
($e:expr) => {
foo!(@internal $e)
};
// Rule nội bộ (quy ước bắt đầu với @)
(@internal $e:expr) => {
$e * 2
};
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
🎯 Best Practices
Khi nào dùng cái nào?
| Đặc điểm | Declarative | Procedural |
|---|---|---|
| Độ phức tạp | Patterns đơn giản | Logic phức tạp |
| Hiệu năng | Biên dịch nhanh | Chậm hơn |
| Debug | Khó | Dễ hơn một chút |
| Use case | DSL, lặp lại | Derive, validation |
Debug Macro
rust
// Mở rộng macros để xem output
cargo expand
// Hoặc dùng unstable feature
#![feature(trace_macros)]
trace_macros!(true);
my_macro!(...);
trace_macros!(false);1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Lỗi thường gặp
rust
// ❌ Trộn expression và statement context
macro_rules! bad {
($e:expr) => {
let x = $e; // Đây là statement, không phải expression
};
}
// ✅ Dùng block để tạo expression
macro_rules! good {
($e:expr) => {
{
let x = $e;
x
}
};
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16