[HN Gopher] Let's write a macro in Rust
       ___________________________________________________________________
        
       Let's write a macro in Rust
        
       Author : hackeryarn
       Score  : 100 points
       Date   : 2025-10-10 15:57 UTC (7 days ago)
        
 (HTM) web link (hackeryarn.com)
 (TXT) w3m dump (hackeryarn.com)
        
       | Y_Y wrote:
       | They should try writing a spellchecker first, I found the article
       | difficult to read because of the high frequency of typos.
        
         | CaptainOfCoit wrote:
         | The spelling mistakes seems almost intentional. I've noticed
         | that a lot of publications that used to publish obvious AI slop
         | now are doing the same, I'm guessing it throws off some "LLM
         | detectors" or similar.
        
         | spelunker wrote:
         | It became distracting after the first two or three spelling
         | errors, agreed.
        
       | 127 wrote:
       | Having done some Rust macros, the entire thing is such a huge
       | hidden wart. Even very simple meta-programming in Rust need to be
       | in their separate crate and has to use very complex syntax and
       | invocation.
       | 
       | Also the whole "don't write macros" is such a hilarious statement
       | given that entire Rust ecosystem is built on them.
        
         | airstrike wrote:
         | I agree Rust macros are cursed in many different ways but
         | 
         |  _> given that entire Rust ecosystem is built on them._
         | 
         | I don't think the people saying "don't write macros" are the
         | same people building an entire ecosystem on top of them
         | 
         | The iced crate has virtually zero macros by design.
        
         | spoiler wrote:
         | Eh. I agree there's complexity in them, and that a lot of the
         | ecosystem indirectly use them because of derive macros, but I
         | wouldn't call it a wart, and the "syntax" for writing proc
         | macros isn't that bad if you use the syn/quote crates. I agree
         | that decl macros usually make me sigh and open the docs though
        
           | nicoburns wrote:
           | > I agree that decl macros usually make me sigh and open the
           | docs though
           | 
           | Declarative macros are very like regex's: both in that at
           | first they seem incredibly dense and arcane but once you work
           | out how to read them they're actually very simple, and in
           | that the syntax is literally similar in that it's a list of
           | tokens that must match sequentially.
        
             | pjmlp wrote:
             | If regexp was simple there would not exist universities
             | dedicating a semester to its theory and various
             | implementation algorithms, alongside a famous quote. :)
        
               | tialaramex wrote:
               | Universities are teaching about the Regular languages and
               | about Finite automata.
               | 
               | So if we're saying this means regex isn't simple then -
               | having observed that my University's Math course has
               | _two_ semester long Number Theory courses we must
               | conclude that the integers aren 't simple either, right ?
               | 
               | And yet, the integers are simple enough that's where we
               | start five year olds, by counting things.
               | 
               | Simple and Shallow are not the same thing.
        
               | pjmlp wrote:
               | Indeed they aren't, that is why math has such a bad
               | reputation in most countries, already at basic school
               | levels.
               | 
               | Recent article this week in German press, as per IQB-
               | Bildungstrend 2024 results, one third of students fail in
               | math exams.
               | 
               | Which reinforces the common culture that math is hard,
               | and it is to be expected not to have great grades at it.
        
         | pjmlp wrote:
         | It is its own can of worms, and maybe it is due to me using the
         | language on and off since 1992, but I find what is possible in
         | C++23, much easier for metaprogramming as the few times I had
         | to understand trying to implement Rust macros.
         | 
         | C++26 reflection will make this much easier, without having to
         | depend in stuff like the syn crate.
         | 
         | I wonder if going with two macro systems, each with its own
         | syntax and approach, was such a good idea.
        
         | IshKebab wrote:
         | Maybe "don't write your own macros". I guess it's like C++
         | template meta programming. If you find yourself using that a
         | lot, it's probably a bad sign. But also Eigen is pretty great.
        
           | TinkersW wrote:
           | I dunno, the C++ template implementation of this macro would
           | be far easier to read as it is just normal C++.
        
             | IshKebab wrote:
             | C++ templates are totally different feature. You couldn't
             | do this with C++ templates.
             | 
             | It was just an example of something that gets very hairy
             | for library implementers but is still quite nice for users.
        
         | ameliaquining wrote:
         | Macros by example, which is what this post is about, don't have
         | to be in a separate crate.
        
       | Communitivity wrote:
       | Bjarne Stroustrup said "C makes it easy to shoot yourself in the
       | foot; C++ makes it harder, but when you do it blows your whole
       | leg off". The same is true of Rust macros. When you need them
       | they're awesome, but you should almost never need them - add a
       | new macro as a very last resort.
        
         | galangalalgol wrote:
         | I agree with this, mostly because all the macros I would use
         | are mostly written already. If const generic expressions
         | arrive, a lot of those could go away too. In languages like
         | lisp or zig where metaprogramming is a central feature, what do
         | they do differently to make it better? Would those things have
         | worked for rust?
        
         | jacquesm wrote:
         | That's a hilarious quote. Thank you.
        
         | tcfhgj wrote:
         | > The same is true of Rust macros.
         | 
         | can't follow
        
       | the__alchemist wrote:
       | I like _using_ or _exposing_ macros when appropriate. They can
       | simplify messy syntax, or prevent repetitions not allowed by the
       | compiler directly. I can 't get the syntax for writing them to
       | stick. So, I am happy with letting LLMs handle this.
       | 
       | Sometimes the macros are for large chunks of code that uses a
       | different type in a key place, or sometimes they're for cleaning
       | up verbose function calls that will be made multiple times, so
       | the important parts (e.g. the params) are obvious, instead of
       | being mixed with boilerplate. In general, they're nice for
       | papering over boilerplate. In particular, there's a pattern in
       | embedded that involves a long expression with
       | `try_into().unwrap()` for parsing bytes that I have a macro for.
       | And another for managing RefCell<Mutex<Option>>> locks.
       | 
       | I prompt the LLM with the working, but repetitive or boilerplate-
       | laden code, and it macros it: "Please write a macro that stops
       | the repetition in this code block: ```rust ```. Here's a example
       | of calling it: `do_thing!(a, b, c)`
       | 
       | Works every time!
        
         | afavour wrote:
         | > I can't get the syntax for writing them to stick
         | 
         | Oh man, same. I wrote a macro crate then went back to it months
         | later and couldn't make heads nor tails of anything I wrote.
         | Something about the syntax just doesn't stick in my brain.
        
       | iambacktomyword wrote:
       | Interesting points -- I always thought macros were core to Rust's
       | design philosophy. Do you think procedural macros will ever get
       | simpler, or is the complexity kind of intentional for safety?
        
       | iambacktomyword wrote:
       | True, macros in Rust can feel heavy, but they also enable some
       | powerful abstractions you just can't get otherwise. I guess the
       | "don't write macros" advice is more about avoiding overuse than
       | rejecting them completely.
        
         | kachapopopow wrote:
         | The only macro I ever use is json! or for applying 10 derives
         | to more than 1 struct
        
       | iambacktomyword wrote:
       | So basically, macros are like dark magic -- everyone warns you
       | not to use them, but half the ecosystem runs on them anyway
        
       | iambacktomyword wrote:
       | Yeah, Rust's macro system feels like a double-edged sword --
       | amazing power, but with a big learning curve. The tooling and
       | syntax definitely make simple meta-programming harder than it
       | should be.
        
         | tracker1 wrote:
         | I liken it to a bit less extreme a version of the "don't write
         | your own cryptography." Macros are definitely useful, and they
         | are pretty widely used and adopted for several things. That
         | said, actually creating your own tends to take a slightly
         | different mindset than general application development.
        
       | tialaramex wrote:
       | So, this article is writing a "by example" macro, and my
       | impression is that many comments here are thinking about Rust's
       | procedural or "proc" macros and most particularly its entirely
       | general procedural macros which are indeed arbitrary code
       | executing in the compiler. That's not what the article is about
       | at all.
       | 
       | "by example" macros are actually just a fancier version of
       | templating systems I'd expect most of you have used. Their
       | biggest fancy feature is probably repetition, it's easy for
       | Rust's macro to say if we've got a list of N things, we're going
       | to emit this same code N times, but with each thing in turn
       | filled out.
       | 
       | The syntax doesn't look that much like the rest of Rust but
       | that's mostly an ergonomic consideration, so you can distinguish
       | between your macro and the code your macro is spitting out. In
       | fact "at least the syntax is the same" is one of the few ways the
       | procedural macros are simpler, since they're literally some Rust
       | run by your compiler.
        
       | BetaDeltaAlpha wrote:
       | There are so many typos in here it makes it difficult to focus on
       | the content. Is this a deliberate signal that the article was
       | human written?
       | 
       | I learned a lot regardless and I'm adding this to my blogroll.
        
       | adregan wrote:
       | One neat thing about the referenced chapter on macros from
       | _Practical Common Lisp_ is that it 's chapters 7 and 8 of a 30+
       | chapter book. You're only learning about variables in the chapter
       | before and then macros. I like to think about that when
       | conversations about macros always devolve into talking about how
       | you should never use macros.
       | 
       | Now apologies for the aside--and I know I'm likely wading into a
       | very expansive topic and reducing it down to something simple--
       | but why in Rust, if the types are known:                   struct
       | Song {           title: String,           artist: String,
       | rating: i64,         }
       | 
       | do you have to call `.to_string()` on things that look a lot like
       | strings already?                   Song::new("Hate
       | Me".to_string(), "Blue October".to_string(), 9)
       | 
       | Couldn't the compiler just do that for you?
        
         | steveklabnik wrote:
         | They're different kinds of strings, the String string means
         | heap allocation, and Rust never allocates in the language, so
         | the compiler automatically invoking allocation routines for you
         | wouldn't be good, in Rust's view.
        
         | throwawaymaths wrote:
         | the reasons to not use macros have to do with hygeinicity? --
         | macros can do things like introduce code that is hard to
         | understand, perhaps a dynamically named function, or a
         | difficult to chase dependency or import... moreover these can
         | make grepping harder.
        
         | LtdJorge wrote:
         | Because to_string allocates. And if a function requires a
         | String (owned), it cannot accept a str reference (borrowed), it
         | would defeat the purpose of the strong type system. String is
         | moved, while str is passed by reference.
         | 
         | There is the exception of Deref. If the function requires type
         | A, and you pass it type B, which Derefs into type A, the
         | compiler will Deref it for you. But that is zero cost and panic
         | free, whereas allocating (and copying) an owned type from a
         | reference isn't. In Rust you have to be explicit.
         | 
         | Anyway, using String in function signatures is most often not
         | the best choice. If you will internally be required to use a
         | String, it's better to ask for a type "impl Into<String>",
         | you'd call into() inside your function. And in the most common
         | case, where you require a &str or can convert to your type from
         | it, the best choice is an "impl AsRef<str>" and you can call
         | as_ref() on it to get a str reference, which you can wrap in a
         | Box, Rc, Arc, String, or your custom type, or pass it directly
         | to other functions. All of those, Box<str>, Rc<str>, etc
         | implement both traits.
         | 
         | Using impl Trait, you avoid having to declare generic
         | parameters.
        
           | gnatolf wrote:
           | If only one could construct a macro to solve the boilerplate
           | of AsRef<str> etc ;)
        
             | LtdJorge wrote:
             | To me, there's not much difference between:
             | fn foo(name: &str) {}
             | 
             | and*:                 fn foo(name: impl AsRef<str>) {}
        
         | marcosdumay wrote:
         | That's the price you pay for a low level language where things
         | like memory usage are visible for you to optimize.
         | 
         | If you don't see the gain, maybe Rust is not the right language
         | for your use-case.
        
         | estebank wrote:
         | To expand on the sibling comments, this is why Rust doesn't do
         | implicit type conversions that could allocate:
         | 
         | https://groups.google.com/a/chromium.org/g/chromium-dev/c/EU...
        
         | rootnod3 wrote:
         | To be fair, as much as Rust macros are nice, it is a losing
         | battle to go up against the Lisp macro system. Lisp might have
         | its flaws, but macros in Lisp are second to none.
         | 
         | When to use them is a whole different story. But examples of
         | macros I like are `when` and `unless`. Yes, simple, but they
         | show a nice example of their power.
         | 
         | For more complicated once, love it or hate it, but the `loop`
         | macro is probably THE prime example of a powerful macro.
        
         | QuantumNomad_ wrote:
         | When you have a &str (like "Blue October") and pass it to
         | something that wants a String, you can do .into() instead of
         | .to_string()
         | 
         | It's shorter to write and takes up a little less space on
         | screen to. I almost always use .into() when like in your
         | example I initialize String field members of a struct in Rust.
        
         | tialaramex wrote:
         | Those "things that look at a lot like strings already" are
         | literals and thus constants, with the type `&'static str` which
         | is how Rust spells an immutable reference to a string slice
         | which lives forever. So, Rust is promising that the string
         | slices "Hate Me" and "Blue October" exist, and in practice
         | probably if you look inside the resulting executable it says
         | "Hate MeBlue October" or similar.
         | 
         | On the other hand the String type is a growable array of bytes
         | used to assemble strings, it's an owning type, so Song will
         | _own_ those two Strings, they can 't go away or be altered via
         | some other force. Owning a string would allow you to add to it,
         | change it, or discard it entirely, you obviously can't do that
         | to the text baked into the executable, so if you want to own a
         | String it will have to be allocated at runtime.
         | 
         | You can also write "Some Words".to_owned() for example to the
         | same effect.
        
       | TheSoftwareGuy wrote:
       | For those interested, this blog post also has a part 2 and 3:
       | 
       | https://hackeryarn.com/post/rust-macros-2/
       | 
       | https://hackeryarn.com/post/rust-macros-3/
        
         | gnatolf wrote:
         | Which confusingly are linked to on the 'prev' link at the
         | bottom of each part...
        
       | stared wrote:
       | `macro_rules!` - I always read "rules" as a verb.
        
         | darkest_ruby wrote:
         | Omg, was it not a verb all this time??!
        
       ___________________________________________________________________
       (page generated 2025-10-17 23:01 UTC)