"Nine Rules for Creating Procedural Macros in Rust: Practical Lessons from anyinput, a New Macro for Easily Accepting String/Path/Iterator/Array-Like Inputs"

Rust's proc macros let us use Rust to write Rust. Sadly, they can be hard to develop and debug because they live in special "proc-macro = true" projects. Happily, crates such as proc-macro2, syn, quote, trybuild, and proc_macro_error can fix the difficulty. With the right setup, you can even set interactive breakpoints and single step through your macro code.

This free article in Towards Data Science/Medium describes the setup I used while developing anyinput. It goes over what I learned with lots of examples and tips in the form of "rules".

I'd love to hear about other folk's experiences. What tips do you have for making macro programming more like regular programming?

-- Carl

p.s. The "Rules":

  1. Use a Rust workspace and proc_macro2 to develop, debug, and unit-test your macro in a normal (non-macro) project.
  2. Use syn, proc_macro2, and quote to convert freely among literal code, tokens, syntax trees, and strings.
  3. Create easily debuggable unit tests that report any differences between what your macro does and what you expect.
  4. Use AST Explorer and the syn documentation to understand Rust syntax trees. Destructure syntax trees with Rust’s pattern matching and struct/enum access.
  5. Construct syntax trees with parse_quote! and Rust’s struct update syntax.
  6. Use syn’s Fold Trait to recursively traverse, destructure, and construct syntax trees.
  7. Use proc_macro_error to return ergonomic and testable errors.
  8. Create integration tests. Include UI tests based on trybuild.
  9. Follow the rules of elegant Rust API design, especially, eating your own dogfood, using Clippy, and writing good documentation.
2 Likes

Just wanted to say thanks -- as a Rust newbie both the article and the "rules of elegant Rust API design" you pointed to look like useful insights.

1 Like

Thanks! Even with writing functions for myself, I like to keep in mind what a nice API design would look like. Some design ideas are so easy, they are worth doing even for private functions.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.