Challenge 3: Why #[derive(Debug)] Must Be Procedural — Possible Solution ==================================================================== #[derive(Debug)] needs to generate an entire impl std::fmt::Debug for YourStruct block, and that generated code has to know SPECIFIC, STRUCTURAL FACTS about the exact struct it's applied to: what its field names actually are, how many fields it has, and what order they appear in — so it can produce output like YourStruct { field_a: ..., field_b: ... } with the real field names embedded directly in the generated formatting code. WHY macro_rules!'S PATTERN-MATCHING APPROACH CAN'T EXPRESS THIS: a declarative macro operates by matching a FIXED set of token PATTERNS supplied directly in its own definition (like $x:expr, or a comma- separated repetition) — it has no way to INSPECT an arbitrary, already-existing struct definition it's attached to and discover "what are this particular struct's field names, generically, for any struct someone might apply this macro to." The struct's shape isn't something being typed directly into the macro invocation the way square!(5)'s "5" is — it's the definition of an entirely separate item elsewhere in the code that the derive macro is being ATTACHED to and needs to introspect. WHY A PROCEDURAL MACRO CAN DO THIS: per this chapter's explanation, a procedural macro is an actual Rust FUNCTION receiving the struct's full TokenStream as input — meaning it receives the complete, unparsed token representation of the struct's definition itself (every field name, every field's type, in order) and can run arbitrary Rust code to walk through and inspect that structure programmatically, then produce a customized impl Debug block as its OUTPUT TokenStream, built specifically around whatever fields it actually found. This requires genuine, general-purpose introspection and code generation logic — exactly the capability #[derive(...)]'s classification as a procedural macro (not declarative) reflects.