Let's Write a Macro in Rust
Key topics
The article discusses writing a macro in Rust, sparking a discussion on the complexity and use cases of Rust's macro system, with some commenters praising their power and others criticizing their difficulty and potential for misuse.
Snapshot generated from the HN discussion
Discussion Activity
Very active discussionFirst comment
7d
Peak period
26
Day 8
Avg / period
15.7
Based on 47 loaded comments
Key moments
- 01Story posted
Oct 10, 2025 at 11:57 AM EDT
3 months ago
Step 01 - 02First comment
Oct 17, 2025 at 7:43 AM EDT
7d after posting
Step 02 - 03Peak activity
26 comments in Day 8
Hottest window of the conversation
Step 03 - 04Latest activity
Oct 19, 2025 at 12:08 AM EDT
3 months ago
Step 04
Generating AI Summary...
Analyzing up to 500 comments to identify key contributors and discussion patterns
Want the full context?
Jump to the original sources
Read the primary article or dive into the live Hacker News thread when you're ready.
Should be a lot better now.
Also the whole "don't write macros" is such a hilarious statement given that entire Rust ecosystem is built on them.
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.
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.
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.
It was just an example of something that gets very hairy for library implementers but is still quite nice for users.
> 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.
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.
can't follow
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!
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.
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:
do you have to call `.to_string()` on things that look a lot like strings already? Couldn't the compiler just do that for you?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.
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.
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.
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.
https://groups.google.com/a/chromium.org/g/chromium-dev/c/EU...
If you don't see the gain, maybe Rust is not the right language for your use-case.
https://hackeryarn.com/post/rust-macros-2/
https://hackeryarn.com/post/rust-macros-3/
"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.
I learned a lot regardless and I'm adding this to my blogroll.