Declarative macros หรือ macro_rules! สร้าง code patterns ที่ซ้ำๆ
// ❌ ซ้ำซาก
let v1 = vec![1, 2, 3];
let v2 = {
let mut temp = Vec::new();
temp.push(1);
temp.push(2);
temp.push(3);
temp
};
// ✅ ใช้ macro - สั้นกว่า
let v1 = vec![1, 2, 3];
+-------------------------------------------------------------------+
| Macro Expansion Process |
+-------------------------------------------------------------------+
| |
| Source Code (you write) Expanded Code (Compiler sees) |
| ------------------- --> -------------------------- |
| |
| vec![1, 2, 3] { |
| | let mut temp = Vec::new(); |
| | temp.push(1); |
| | temp.push(2); |
| | temp.push(3); |
| | temp |
| +------------------------> } |
| |
| println!("x = {}", x) std::io::_print( |
| | format_args!("x = {}", x) |
| +------------------------> ) |
| |
+-------------------------------------------------------------------+
| Pattern | ความหมาย | ตัวอย่าง |
$x:expr | Expression ใดๆ | 1 + 2, foo() |
$x:ident | Identifier | my_var, foo |
$x:ty | Type | i32, String |
$($x:expr),* | Repeat 0+ ครั้ง | 1, 2, 3 |
$($x:expr),+ | Repeat 1+ ครั้ง | ต้องมีอย่างน้อย 1 |
$($x:expr);* | Repeat คั่นด้วย ; | a; b; c |
macro_rules! macro_name {
( pattern ) => {
expansion
};
}
macro_rules! say_hello {
() => {
println!("Hello!");
};
}
fn main() {
say_hello!(); // Hello!
}
macro_rules! greet {
($name:expr) => {
println!("Hello, {}!", $name);
};
}
fn main() {
greet!("World"); // Hello, World!
greet!("Rust"); // Hello, Rust!
greet!(1 + 2); // Hello, 3!
}
| Designator | Matches |
expr | Expression เช่น 1 + 2, x |
ident | Identifier เช่น foo, x |
ty | Type เช่น i32, String |
pat | Pattern เช่น Some(x), _ |
block | Block { ... } |
stmt | Statement |
literal | Literal เช่น "hello", 42 |
tt | Token tree (ทุกอย่าง) |
macro_rules! calculate {
(add $a:expr, $b:expr) => {
$a + $b
};
(sub $a:expr, $b:expr) => {
$a - $b
};
(mul $a:expr, $b:expr) => {
$a * $b
};
}
fn main() {
println!("{}", calculate!(add 1, 2)); // 3
println!("{}", calculate!(sub 5, 3)); // 2
println!("{}", calculate!(mul 4, 5)); // 20
}
ใช้ $(...)* หรือ $(...)+ สำหรับซ้ำ:
macro_rules! vec_of_strings {
// ( pattern ),* = zero or more, comma separated
($($x:expr),*) => {
{
let mut temp = Vec::new();
$(
temp.push($x.to_string());
)*
temp
}
};
}
fn main() {
let v = vec_of_strings!["a", "b", "c"];
println!("{:?}", v); // ["a", "b", "c"]
let v2 = vec_of_strings![1, 2, 3];
println!("{:?}", v2); // ["1", "2", "3"]
}
| Operator | Meaning |
* | Zero or more |
+ | One or more |
? | Zero or one |
macro_rules! hashmap {
($($key:expr => $value:expr),* $(,)?) => {
{
let mut map = std::collections::HashMap::new();
$(
map.insert($key, $value);
)*
map
}
};
}
fn main() {
let scores = hashmap! {
"Alice" => 100,
"Bob" => 85,
"Charlie" => 90,
};
println!("{:?}", scores);
}
macro_rules! debug_var {
($var:expr) => {
println!("{} = {:?}", stringify!($var), $var);
};
}
fn main() {
let x = 42;
let name = "Rust";
let vec = vec![1, 2, 3];
debug_var!(x); // x = 42
debug_var!(name); // name = "Rust"
debug_var!(vec); // vec = [1, 2, 3]
}
stringify! แปลง expression เป็น string literal
macro_rules! create_function {
($func_name:ident) => {
fn $func_name() {
println!("Called {:?}()", stringify!($func_name));
}
};
}
create_function!(foo);
create_function!(bar);
fn main() {
foo(); // Called "foo"()
bar(); // Called "bar"()
}
- สร้าง macro
min! ที่หาค่าต่ำสุดของ 2 ค่า
- สร้าง macro
println_all! ที่ print หลายค่า
- สร้าง macro ที่สร้าง struct
สร้าง HashMap แบบสั้นๆ เหมือน vec!:
macro_rules! hashmap {
// Empty hashmap
() => {
std::collections::HashMap::new()
};
// With key-value pairs
($($key:expr => $value:expr),+ $(,)?) => {{
let mut map = std::collections::HashMap::new();
$(
map.insert($key, $value);
)+
map
}};
}
fn main() {
// ใช้งาน macro
let scores = hashmap! {
"Alice" => 100,
"Bob" => 85,
"Charlie" => 92,
};
println!("{:?}", scores);
// {"Alice": 100, "Bob": 85, "Charlie": 92}
}
| Pattern | Use Case | Example |
| Literal | สร้าง collection | vec!, hashmap! |
| DSL | Domain-specific syntax | html!, sql! |
| Code Gen | สร้าง boilerplate | #[derive(...)] |
| Assertion | Testing | assert!, assert_eq! |
| Aspect | Macros | Functions |
| Evaluation | Compile-time | Runtime |
| Types | ไม่ต้องระบุ | ต้องระบุ |
| Arguments | Variable | Fixed |
| Syntax | Flexible | Fixed |
| Debug | ยากกว่า | ง่ายกว่า |
| Pattern | Meaning |
$x:expr | Match expression |
$x:ident | Match identifier |
$($x:expr),* | Zero or more exprs |
$($x:expr),+ | One or more exprs |
$($x:expr)? | Optional expr |
stringify!($x) | Convert to string |
👉 ต่อไป: Procedural Macros