<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Macros on</title><link>/tags/macros/</link><description>Recent content in Macros on</description><generator>Hugo</generator><language>en</language><lastBuildDate>Tue, 04 Mar 2025 12:50:00 +0000</lastBuildDate><atom:link href="/tags/macros/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 12: Macro Anti-Patterns — When not to macro</title><link>/post/rust/rust-macros-anti-patterns/</link><pubDate>Tue, 04 Mar 2025 12:50:00 +0000</pubDate><guid>/post/rust/rust-macros-anti-patterns/</guid><description>&lt;p&gt;I once worked on a codebase where someone had written a macro for everything. Creating structs? Macro. Implementing a two-line function? Macro. Logging? Custom logging macro that wrapped &lt;code&gt;println!&lt;/code&gt; and added a timestamp. The macro definitions file was 800 lines long. The macros had macros inside them. Nobody on the team could modify them without breaking something, and the original author had left six months earlier. That project taught me more about when &lt;em&gt;not&lt;/em&gt; to use macros than any tutorial ever could.&lt;/p&gt;</description></item><item><title>Lesson 11: Real-World Macro Patterns — serde, clap, sqlx under the hood</title><link>/post/rust/rust-macros-real-world/</link><pubDate>Sat, 01 Mar 2025 15:40:00 +0000</pubDate><guid>/post/rust/rust-macros-real-world/</guid><description>&lt;p&gt;I used &lt;code&gt;#[derive(Serialize)]&lt;/code&gt; for two years before I actually looked at what it generates. When I finally ran &lt;code&gt;cargo expand&lt;/code&gt; on a struct with five fields, I got 150 lines of serialization code — visitor patterns, generic bounds, field-by-field traversal, error handling. All generated from a single line. Understanding how production crates use macros changed how I think about API design. These aren&amp;rsquo;t academic exercises — they&amp;rsquo;re the patterns behind the most downloaded crates in the ecosystem.&lt;/p&gt;</description></item><item><title>Lesson 10: syn and quote — Parsing and generating tokens</title><link>/post/rust/rust-macros-syn-quote/</link><pubDate>Thu, 27 Feb 2025 10:20:00 +0000</pubDate><guid>/post/rust/rust-macros-syn-quote/</guid><description>&lt;p&gt;Every time I write a proc macro without &lt;code&gt;syn&lt;/code&gt; and &lt;code&gt;quote&lt;/code&gt;, I regret it within twenty minutes. Raw &lt;code&gt;TokenStream&lt;/code&gt; manipulation is like writing HTML by concatenating strings — technically possible, practically unbearable. These two crates are the reason Rust&amp;rsquo;s proc macro ecosystem works at all. They handle the two hardest parts — parsing Rust syntax into a usable data structure, and generating valid Rust code from a template — so you can focus on the actual logic of your macro.&lt;/p&gt;</description></item><item><title>Lesson 9: Function-Like Proc Macros — sql!() and friends</title><link>/post/rust/rust-macros-function-like/</link><pubDate>Mon, 24 Feb 2025 18:30:00 +0000</pubDate><guid>/post/rust/rust-macros-function-like/</guid><description>&lt;p&gt;A colleague once asked me why &lt;code&gt;sqlx::query!(&amp;quot;SELECT * FROM users WHERE id = $1&amp;quot;)&lt;/code&gt; can catch SQL errors at compile time. &amp;ldquo;Is it reading the database during compilation?&amp;rdquo; Yes. It literally connects to your database, validates the query, checks the types, and generates type-safe Rust code — all before your program runs. That&amp;rsquo;s a function-like proc macro doing things that feel illegal. And the mechanics behind it are more straightforward than you&amp;rsquo;d think.&lt;/p&gt;</description></item><item><title>Lesson 8: Attribute Macros — #[my_attr] in practice</title><link>/post/rust/rust-macros-attribute/</link><pubDate>Sat, 22 Feb 2025 07:15:00 +0000</pubDate><guid>/post/rust/rust-macros-attribute/</guid><description>&lt;p&gt;I was reviewing a codebase that used Actix Web and kept seeing &lt;code&gt;#[get(&amp;quot;/users&amp;quot;)]&lt;/code&gt; on handler functions. I knew it was a macro, but I didn&amp;rsquo;t understand the mechanics — how does an attribute on a function transform the function? Where does the routing registration happen? When I finally built my own attribute macro, the whole system clicked. Attribute macros aren&amp;rsquo;t magic. They&amp;rsquo;re just functions that receive code and return different code.&lt;/p&gt;</description></item><item><title>Lesson 7: Derive Macros — Custom #[derive()]</title><link>/post/rust/rust-macros-derive/</link><pubDate>Wed, 19 Feb 2025 13:55:00 +0000</pubDate><guid>/post/rust/rust-macros-derive/</guid><description>&lt;p&gt;The first derive macro I shipped to production generated about 200 lines of boilerplate per struct. We had 47 structs. That&amp;rsquo;s 9,400 lines of code I didn&amp;rsquo;t have to write, test, or maintain. Every time someone added a field to a struct, the derive macro picked it up automatically. No manual updates, no forgotten implementations, no &amp;ldquo;oh I changed the struct but forgot to update the builder&amp;rdquo; bugs. Derive macros are the highest-leverage tool in Rust&amp;rsquo;s macro system, and once you build one, you&amp;rsquo;ll find excuses to build more.&lt;/p&gt;</description></item><item><title>Lesson 6: Procedural Macros — The three kinds</title><link>/post/rust/rust-macros-proc-intro/</link><pubDate>Mon, 17 Feb 2025 09:40:00 +0000</pubDate><guid>/post/rust/rust-macros-proc-intro/</guid><description>&lt;p&gt;The moment I realized &lt;code&gt;macro_rules!&lt;/code&gt; couldn&amp;rsquo;t generate new identifier names from captured inputs, I knew I needed something more powerful. I was trying to auto-generate a &lt;code&gt;_builder&lt;/code&gt; suffix for struct names — take &lt;code&gt;Config&lt;/code&gt; and produce &lt;code&gt;ConfigBuilder&lt;/code&gt;. Declarative macros can&amp;rsquo;t do string manipulation on identifiers. Period. That&amp;rsquo;s what pushed me into procedural macros, and honestly, it felt like unlocking a completely different layer of the language.&lt;/p&gt;
&lt;h2 id="what-procedural-macros-actually-are"&gt;What Procedural Macros Actually Are&lt;/h2&gt;
&lt;p&gt;A procedural macro is a Rust function that runs at compile time. It receives a stream of tokens as input, does whatever processing it wants — parsing, analyzing, transforming — and returns a new stream of tokens as output. The compiler then compiles the returned tokens as if they were regular source code.&lt;/p&gt;</description></item><item><title>Lesson 5: Debugging Macros — cargo-expand and trace_macros</title><link>/post/rust/rust-macros-debugging/</link><pubDate>Fri, 14 Feb 2025 11:05:00 +0000</pubDate><guid>/post/rust/rust-macros-debugging/</guid><description>&lt;p&gt;You will write a macro that compiles, runs, and produces the wrong output. You&amp;rsquo;ll stare at the macro definition, convinced it&amp;rsquo;s correct. You&amp;rsquo;ll re-read the pattern matching rules, check the repetition operators, verify the fragment specifiers — everything looks right. And then you&amp;rsquo;ll expand the macro and realize it&amp;rsquo;s generating something completely different from what you imagined. This has happened to me more times than I&amp;rsquo;m willing to admit.&lt;/p&gt;
&lt;p&gt;Macro debugging is a different skill from regular debugging. You can&amp;rsquo;t set breakpoints in macro expansion. You can&amp;rsquo;t step through it. You have to &lt;em&gt;see&lt;/em&gt; the generated code, and then reason backwards from there to figure out where the pattern matching went wrong.&lt;/p&gt;</description></item><item><title>Lesson 4: Macro Hygiene — Scoping and naming pitfalls</title><link>/post/rust/rust-macros-hygiene/</link><pubDate>Wed, 12 Feb 2025 16:10:00 +0000</pubDate><guid>/post/rust/rust-macros-hygiene/</guid><description>&lt;p&gt;I once spent an embarrassing amount of time debugging a macro that worked perfectly in one file and broke in another. Same macro, same input, different behavior. Turned out the expansion was referencing a variable called &lt;code&gt;result&lt;/code&gt; — which happened to shadow a &lt;code&gt;result&lt;/code&gt; variable at the call site. The macro was hygienically correct in isolation but collided with the caller&amp;rsquo;s namespace. That&amp;rsquo;s when I actually understood what &amp;ldquo;macro hygiene&amp;rdquo; means and why Rust only gets it partially right.&lt;/p&gt;</description></item><item><title>Lesson 3: Macro Pattern Matching — Repetition, fragments, and captures</title><link>/post/rust/rust-macros-patterns/</link><pubDate>Mon, 10 Feb 2025 08:30:00 +0000</pubDate><guid>/post/rust/rust-macros-patterns/</guid><description>&lt;p&gt;There&amp;rsquo;s a point when writing declarative macros where the syntax stops feeling like Rust and starts feeling like regex for code. You&amp;rsquo;re stacking repetition operators, nesting captures inside captures, and using &lt;code&gt;tt&lt;/code&gt; munching to parse things the macro system was never designed to parse. It&amp;rsquo;s weird, it&amp;rsquo;s powerful, and once it clicks, you&amp;rsquo;ll wonder why you ever wrote boilerplate by hand.&lt;/p&gt;
&lt;h2 id="repetition-operators-deep-dive"&gt;Repetition Operators Deep Dive&lt;/h2&gt;
&lt;p&gt;We touched on repetition in the last lesson. Now let&amp;rsquo;s get into the mechanics that actually matter when you&amp;rsquo;re building non-trivial macros.&lt;/p&gt;</description></item><item><title>Lesson 2: Declarative Macros — macro_rules! from zero</title><link>/post/rust/rust-macros-declarative/</link><pubDate>Fri, 07 Feb 2025 14:45:00 +0000</pubDate><guid>/post/rust/rust-macros-declarative/</guid><description>&lt;p&gt;The first macro I ever wrote was a disaster. I wanted a shorthand for creating &lt;code&gt;HashMap&lt;/code&gt;s — something like &lt;code&gt;map!{ &amp;quot;a&amp;quot; =&amp;gt; 1, &amp;quot;b&amp;quot; =&amp;gt; 2 }&lt;/code&gt;. The macro compiled. The expansion was garbage. I didn&amp;rsquo;t understand fragment specifiers, I didn&amp;rsquo;t understand repetition, and I definitely didn&amp;rsquo;t understand why the compiler kept telling me &amp;ldquo;unexpected token.&amp;rdquo; Took me a full weekend to get it right. Let me save you that weekend.&lt;/p&gt;</description></item><item><title>Lesson 1: Why Macros — When functions aren't enough</title><link>/post/rust/rust-macros-why/</link><pubDate>Wed, 05 Feb 2025 10:22:00 +0000</pubDate><guid>/post/rust/rust-macros-why/</guid><description>&lt;p&gt;I spent three hours once writing nearly identical &lt;code&gt;impl&lt;/code&gt; blocks for sixteen different numeric types. Copy, paste, change &lt;code&gt;i32&lt;/code&gt; to &lt;code&gt;i64&lt;/code&gt;, change &lt;code&gt;i64&lt;/code&gt; to &lt;code&gt;u32&lt;/code&gt;, repeat. Halfway through I started making typos. By the end I had a bug in the &lt;code&gt;u128&lt;/code&gt; variant that took another hour to find. That&amp;rsquo;s the day I actually sat down and learned macros properly.&lt;/p&gt;
&lt;h2 id="the-gap-between-functions-and-macros"&gt;The Gap Between Functions and Macros&lt;/h2&gt;
&lt;p&gt;Functions are great. You take some inputs, do some work, return a result. But functions operate on &lt;em&gt;values&lt;/em&gt;. They can&amp;rsquo;t generate new struct definitions. They can&amp;rsquo;t implement traits for you. They can&amp;rsquo;t vary the number or types of arguments they accept. They can&amp;rsquo;t produce different code depending on what you pass them at compile time.&lt;/p&gt;</description></item></channel></rss>