ข้ามไปยังเนื้อหา

Macros

ไม่มีคู่เทียบตรงๆ ใน TypeScript — แต่ decorators ใกล้เคียงที่สุด

หัวข้อที่มีชื่อว่า “ไม่มีคู่เทียบตรงๆ ใน TypeScript — แต่ decorators ใกล้เคียงที่สุด”

Rust macros ไม่มีคู่เทียบเดียวใน TypeScript คู่เทียบที่ใกล้เคียงที่สุด:

  • TypeScript decorators (@Injectable(), @Column()) — คล้าย #[derive(...)] ตรงที่ใช้ annotate code แล้ว trigger behaviour ที่ generate ขึ้นมา
  • Template literal types / type-level metaprogramming — เหมือนกับ macro_rules! ทำงานบน syntax ก่อน runtime
  • Babel / esbuild plugins — transformations ที่ apply ตอน build time

ความแตกต่างหลัก: Rust macro เป็น hygienic และทำงานบน AST เป็นส่วนหนึ่งของตัวภาษาเอง ไม่ใช่ระบบ annotation ที่แปะเพิ่มทีหลัง แถมทำงานตอน compile time โดยเข้าถึง token tree ของ compiler ได้เต็มที่

ประเภทSyntaxทำอะไร
Declarative (macro_rules!)macro_rules! my_macro { ... }Pattern-match บน token trees และ generate code
Procedural (#[derive], #[attribute], function-like)#[derive(Debug, Clone)]รับ AST node และ emit AST nodes ใหม่

บทเรียนนี้เน้น macro_rules! (ประเภทที่ง่ายกว่า) และ derive macros (ซึ่งคุณได้ใช้แล้วโดยไม่รู้ตัว)

macro_rules! ทำงานเหมือน match expression สำหรับ syntax แต่ละ arm กำหนด pattern ของ tokens ที่จะ match และ template ของ tokens ที่จะ emit

TypeScript
// TypeScript — no direct equivalent.
// Closest: a generic utility function or a build-time code generator.
function repeat<T>(value: T, times: number): T[] {
return Array.from({ length: times }, () => value);
}
console.log(repeat("hi", 3)); // ["hi", "hi", "hi"]
// TypeScript decorators (stage 3) annotate classes at runtime:
// @Injectable() class MyService { ... }
Rust
// macro_rules! — match on syntax, emit code
macro_rules! say_hello {
// arm 1: no arguments
() => {
println!("Hello!");
};
// arm 2: one expression argument
($name:expr) => {
println!("Hello, {}!", $name);
};
}
// #[derive] macro — auto-implement traits at compile time
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: f64,
y: f64,
}
fn main() {
say_hello!();
say_hello!("Rustacean");
let p1 = Point { x: 1.0, y: 2.0 };
let p2 = p1.clone(); // Clone derived
println!("{:?}", p1); // Debug derived
println!("p1 == p2: {}", p1 == p2); // PartialEq derived
}

vec![1, 2, 3] เป็น macro_rules! macro จาก standard library ตอน compile จะ expand เป็นโค้ดที่สร้าง Vec, เรียก push ทีละ element แล้ว return vector ออกมา ถ้าไม่มี macro ตัวนี้คุณต้องเขียนแบบนี้:

let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);

println! และ format! ก็เป็น declarative macro เช่นกัน — parse format string ตอน compile time แล้ว generate โค้ด formatting ที่ type-safe ออกมา

ทุกครั้งที่คุณเขียน #[derive(Debug, Clone, Serialize, Deserialize)] คุณกำลัง invoke procedural macro สิ่งเหล่านี้มีพลังมากกว่า macro_rules! เพราะรับ AST ทั้งหมดของ struct หรือ enum และ emit code ใหม่ที่ต้องการ

Deriveสิ่งที่ generate
Debugfmt::Debug impl — เปิดใช้ {:?} printing
Cloneclone() method
PartialEq / Eqoperators == และ !=
PartialOrd / Ord<, >, <=, >=
serde::Serialize / DeserializeJSON (และ format อื่น) serialisation
thiserror::ErrorDisplay และ From impls สำหรับ error enums
macro_rules! say_hello {
() => {
println!("Hello!");
};
($name:expr) => {
println!("Hello, {}!", $name);
};
}
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: f64,
y: f64,
}
fn main() {
say_hello!();
say_hello!("Rustacean");
let p1 = Point { x: 1.0, y: 2.0 };
let p2 = p1.clone();
println!("{:?}", p1);
println!("p1 == p2: {}", p1 == p2);
}
`#[derive(Debug)]` ทำอะไร?
ฟีเจอร์ใดของ TypeScript ที่คล้ายกับ `#[derive(...)]` ของ Rust มากที่สุด?
'macro hygiene' ใน Rust คืออะไร?