{"id":24383600,"url":"https://github.com/1git2clone/proc-macro-example","last_synced_at":"2025-10-23T16:23:45.515Z","repository":{"id":272335354,"uuid":"916152322","full_name":"1Git2Clone/proc-macro-example","owner":"1Git2Clone","description":"A short getting started on derive macros guide in Rust.","archived":false,"fork":false,"pushed_at":"2025-02-19T12:57:52.000Z","size":543,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-19T13:41:54.283Z","etag":null,"topics":["compile-time-meta-programming","compile-time-reflection","guide","proc-macro","proc-macro-derive","reflection","rust","tutorial"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/1Git2Clone.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-13T14:56:40.000Z","updated_at":"2025-02-19T12:57:55.000Z","dependencies_parsed_at":"2025-01-13T19:36:09.379Z","dependency_job_id":"4cf09d04-ea87-4320-aa0b-143f8de94d39","html_url":"https://github.com/1Git2Clone/proc-macro-example","commit_stats":null,"previous_names":["1git2clone/proc-macro-example"],"tags_count":0,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1Git2Clone%2Fproc-macro-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1Git2Clone%2Fproc-macro-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1Git2Clone%2Fproc-macro-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1Git2Clone%2Fproc-macro-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1Git2Clone","download_url":"https://codeload.github.com/1Git2Clone/proc-macro-example/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243259100,"owners_count":20262403,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compile-time-meta-programming","compile-time-reflection","guide","proc-macro","proc-macro-derive","reflection","rust","tutorial"],"created_at":"2025-01-19T10:15:02.247Z","updated_at":"2025-10-23T16:23:45.439Z","avatar_url":"https://github.com/1Git2Clone.png","language":"Rust","readme":"# Derive macro example\n\n[![Build Icon]][Build Status]\u0026emsp;[![License CC0 Icon]][LICENSE CC0]\u0026emsp;[![License Unlicense Icon]][License Unlicense]\n\n[Build Icon]: https://img.shields.io/github/actions/workflow/status/1git2clone/proc-macro-example/build.yml?branch=main\n[Build Status]: https://github.com/1git2clone/proc-macro-example/actions?query=branch%3Amain\n[License CC0 Icon]: https://img.shields.io/badge/license-CC0_1.0_Universal-blue.svg\n[License CC0]: LICENSE-CC0\n[License Unlicense Icon]: https://img.shields.io/badge/license-Unlicense-blue.svg\n[License Unlicense]: LICENSE-UNLICENSE\n\nA short getting started on derive macros guide in Rust.\n\n\u003e [!IMPORTANT]\n\u003e This assumes you're familiar with Rust's declarative macros. Or at least\n\u003e knowledgeable of programming language syntax and meaning of words like:\n\u003e `identifier` and `expression`.[^1] [^2] [^3] [^4] [^5]\n\n[^1]: \u003chttps://doc.rust-lang.org/reference/macros-by-example.html#metavariables\u003e\n\n[^2]: \u003chttps://doc.rust-lang.org/reference/items.html\u003e\n\n[^3]: \u003chttps://doc.rust-lang.org/reference/attributes.html\u003e\n\n[^4]: \u003chttps://doc.rust-lang.org/reference/statements-and-expressions.html\u003e\n\n[^5]: \u003chttps://doc.rust-lang.org/reference/names.html\u003e\n\n## Table of Contents\n\n---\n\n- [Derive macro example](#derive-macro-example)\n  - [Table of Contents](#table-of-contents)\n  - [What will be discussed](#what-will-be-discussed)\n    - [Front-end, back-end and intermediate representation](#front-end-back-end-and-intermediate-representation)\n  - [What will be done](#what-will-be-done)\n    - [Usage example](#usage-example)\n  - [Insight and resources](#insight-and-resources)\n    - [Source code sources](#source-code-sources)\n    - [Video sources](#video-sources)\n  - [License](#license)\n\n---\n\n## What will be discussed\n\n1. A quick introduction to the three main types of declarative macros[^6]:\n\n   - Function-like - `my_function_like_macro!(...)` **(Not to be confused with\n     declarative macros)**.\n   - Derive - `#[derive(MyDeriveMacro)]` _(We'll focus on this one)_.\n   - Attribute - `#[my_attribute_macro]`.\n\n2. How to manage your thought process about the structuring of your crates and\n   the structure of the macro.\n3. Understanding the process of creating a procedural macro and how it varies\n   from declarative macros.\n4. Introduction to the common tools to achieve the task: the\n   [`syn`](https://docs.rs/syn/ \"docs.rs/syn\") and\n   [`quote`](https://docs.rs/quote/ \"docs.rs/quote\") crates.\n\n[^6]: \u003chttps://doc.rust-lang.org/reference/procedural-macros.html#r-macro.proc.intro\u003e\n\n### Front-end, back-end and intermediate representation\n\nSimilarly to how compilers work, procedural macros work by taking some input,\ndoing something with it, then generating new input for our main program. This\nis also the reason procedural macros need a separate crate in order to work.\n\nFortunately, most of the heavy lifting (parsing and generating Rust code) is\nalready done with the help of `syn` and `quote`. This means that for Rust code\ngeneration, we can focus on the logic behind what we want to achieve more than\nmaking a parser for the complex syntax of the language.\n\n![Visual representation of syn being the front-end and quote as the back-end](./assets/front-end-ir-back-end.png \"Visual representation of syn being the front-end and quote as the back-end\")\n\n1. Syn handles the parsing (usually as\n   [`syn::DeriveInput`](https://docs.rs/syn/latest/syn/struct.DeriveInput.html)\n   for derive macros).\n2. We work with the parsed data.\n3. Our work gets tokenized by the\n   [`quote::quote!`](https://docs.rs/quote/latest/quote/macro.quote.html)\n   macro.\n\n## What will be done\n\nAn example project using a common pattern called: [`Reflective\nprogramming`](https://en.wikipedia.org/wiki/Reflective_programming). The derive\nmacro will work on structs and enums and add a method to them called:\n`get_fields()` for structs and `get_variants()` for enums.\n\n\u003e \"How does this reflect in the real world?\"\n\nRetrieving field names is a commonly used thing when we want to represent a\nRust structure in a different format. This is the default way of\n[`serde`](https://docs.rs/serde/ \"docs.rs/serde\")\n([serde_derive/src/pretend.rs#64-76](https://docs.rs/serde_derive/1.0.217/src/serde_derive/pretend.rs.html#64-76))\nto serialize your struct fields and enum variants.\n\n### Usage example\n\nHere's an example of how `serde` would serialize a `User` object defined like so:\n\n![Code Example of a serde Serialze struct into JSON](./assets/serde-example.png \"Code Example of a serde Serialze struct into JSON\")\n\n\u003c!-- markdownlint-disable MD013 --\u003e\n\u003c!-- Reason: Rust's format standard is 100 column lines. --\u003e\n\n```rust\nuse proc_macro_example_derive::Reflective;\n\n#[derive(serde::Serialize, Reflective)]\nstruct User {\n    pub user_id: u64,\n    pub username: String,\n    pub age: u32,\n}\n\nlet some_user = User {\n    user_id: 1234,\n    username: String::from(\"Harry\"),\n    age: 41,\n};\nlet fields = User::get_fields();\n//           ^--------------- How convenient. This is a good example of how this macro can be used\n//           to streamline the testing process.\nlet expected = format!(\n    r#\"{{\"{}\":{},\"{}\":\"{}\",\"{}\":{}}}\"#,\n    fields[0], some_user.user_id, fields[1], \u0026some_user.username, fields[2], some_user.age\n);\nassert_eq!(serde_json::to_string(\u0026some_user).unwrap(), expected);\n```\n\n\u003c!-- markdownlint-enable MD013 --\u003e\n\n## Insight and resources\n\nIf you've used [`yew`](https://docs.rs/yew/ \"docs.rs/yew\"),\n[`leptos`](https://docs.rs/leptos/ \"docs.rs/leptos\") or any other web\ndevelopment library, then you'd know you can parse any `TokenStream` into a\nRust `TokenStream`. This example doesn't go that in-depth with what you can do\nsince in practice it's possible to make a programming language with Rust's proc\nmacros. In fact, people have also done that with a\n[Python interpreter written in Rust](https://github.com/RustPython/RustPython \"GitHub/RustPython/RustPython\")\nor [Python bindings in Rust](https://github.com/PyO3/pyo3 \"GitHub/PyO3/pyo3\").\n\nThe possibilities are endless and this example just scratches the surface of\nprocedural macros.\n\n### Source code sources\n\nSome other projects whose procedural macros you can check out are:\n\n\u003c!-- markdownlint-disable MD013 --\u003e\n\n- [the `synstructure` crate](https://github.com/mystor/synstructure \"GitHub/mystor/synstructure\").\n- [the `actix_web` crate](https://github.com/search?q=repo%3Aactix%2Factix-web+proc_macro\u0026type=code \"`proc_macro` source code search results\").\n- [the `poise` crate](https://github.com/serenity-rs/poise/blob/current/macros/src/lib.rs \"GitHub/serenity-rs/poise/macros/lib.rs\").\n- [the `darling` and `darling_core` crates](https://github.com/search?q=repo%3ATedDriggs%2Fdarling%20proc_macro\u0026type=code \"`proc_macro` source code search results\").\n\n\u003c!-- markdownlint-enable MD013 --\u003e\n\n### Video sources\n\n- [Comprehending Proc Macros](https://www.youtube.com/watch?v=SMCRQj9Hbx8)\n  by [Logan Smith](https://www.youtube.com/@_noisecode)\n  _(also the main inspiration for this tutorial)_.\n\n- [Rust's Witchcraft](https://www.youtube.com/watch?v=MWRPYBoCEaY)\n  by [No Boilerplate](https://www.youtube.com/@NoBoilerplate).\n\n## License\n\nThis repository is public domain and dual licensed with the\n[CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/legalcode.en)\nlicense or\n[The Unlicense](https://opensource.org/license/unlicense)\nlisence.\n\nYou're free to choose which one to use.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1git2clone%2Fproc-macro-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1git2clone%2Fproc-macro-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1git2clone%2Fproc-macro-example/lists"}