[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-5751":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":15,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":21,"hasPages":21,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":16,"starSnapshotCount":16,"syncStatus":15,"lastSyncTime":27,"discoverSource":28},5751,"proc-macro-workshop","dtolnay\u002Fproc-macro-workshop","dtolnay","Learn to write Rust procedural macros  [Rust Latam conference, Montevideo Uruguay, March 2019]","",null,"Rust",4814,1214,36,2,0,7,18,31.25,"Apache License 2.0",false,"master",[],"2026-06-12 02:01:14","# Rust Latam: procedural macros workshop\n\n*This repo contains a selection of projects designed to learn to write Rust\nprocedural macros &mdash; Rust code that generates Rust code.*\n\n*Each of these projects is drawn closely from a compelling real use case. Out of\nthe 5 projects here, 3 are macros that I have personally implemented in\nindustrial codebases for work, and the other 2 exist as libraries on crates.io\nby other authors.*\n\n\u003Cbr>\n\n## Contents\n\n- [**Suggested prerequisites**](#suggested-prerequisites)\n- [**Projects**](#projects) — Introduction to each of the projects\n  - [**Derive macro:** `derive(Builder)`](#derive-macro-derivebuilder)\n  - [**Derive macro:** `derive(CustomDebug)`](#derive-macro-derivecustomdebug)\n  - [**Function-like macro:** `seq!`](#function-like-macro-seq)\n  - [**Attribute macro:** `#[sorted]`](#attribute-macro-sorted)\n  - [**Attribute macro:** `#[bitfield]`](#attribute-macro-bitfield)\n  - [**Project recommendations**](#project-recommendations) — What to work on\n    depending on your interests\n- [**Test harness**](#test-harness) — Explanation of how testing is set up\n- [**Workflow**](#workflow) — Recommended way to work through the workshop\n- [**Debugging tips**](#debugging-tips)\n\n\u003Cbr>\n\n## Suggested prerequisites\n\nThis workshop covers attribute macros, derive macros, and function-like\nprocedural macros.\n\nBe aware that the content of the workshop and the explanations in this repo will\nassume a working understanding of structs, enums, traits, trait impls, generic\nparameters, and trait bounds. You are welcome to dive into the workshop with any\nlevel of experience with Rust, but you may find that these basics are far easier\nto learn for the first time outside of the context of macros.\n\n\u003Cbr>\n\n## Projects\n\nHere is an introduction to each of the projects. At the bottom, I give\nrecommendations for what order to tackle them based on your interests. Note that\neach of these projects goes into more depth than what is described in the\nintroduction here.\n\n### Derive macro: `derive(Builder)`\n\nThis macro generates the boilerplate code involved in implementing the [builder\npattern] in Rust. Builders are a mechanism for instantiating structs, especially\nstructs with many fields, and especially if many of those fields are optional or\nthe set of fields may need to grow backward compatibly over time.\n\n[builder pattern]: https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FBuilder_pattern\n\nThere are a few different possibilities for expressing builders in Rust. Unless\nyou have a strong pre-existing preference, to keep things simple for this\nproject I would recommend following the example of the standard library's\n[`std::process::Command`] builder in which the setter methods each receive and\nreturn `&mut self` to allow chained method calls.\n\n[`std::process::Command`]: https:\u002F\u002Fdoc.rust-lang.org\u002Fstd\u002Fprocess\u002Fstruct.Command.html\n\nCallers will invoke the macro as follows.\n\n```rust\nuse derive_builder::Builder;\n\n#[derive(Builder)]\npub struct Command {\n    executable: String,\n    #[builder(each = \"arg\")]\n    args: Vec\u003CString>,\n    current_dir: Option\u003CString>,\n}\n\nfn main() {\n    let command = Command::builder()\n        .executable(\"cargo\".to_owned())\n        .arg(\"build\".to_owned())\n        .arg(\"--release\".to_owned())\n        .build()\n        .unwrap();\n\n    assert_eq!(command.executable, \"cargo\");\n}\n```\n\nThis project covers:\n\n- traversing syntax trees;\n- constructing output source code;\n- processing helper attributes to customize the generated code.\n\n*Project skeleton is located under the \u003Ckbd>builder\u003C\u002Fkbd> directory.*\n\n### Derive macro: `derive(CustomDebug)`\n\nThis macro implements a derive for the standard library [`std::fmt::Debug`]\ntrait that is more customizable than the similar `Debug` derive macro exposed by\nthe standard library.\n\n[`std::fmt::Debug`]: https:\u002F\u002Fdoc.rust-lang.org\u002Fstd\u002Ffmt\u002Ftrait.Debug.html\n\nIn particular, we'd like to be able to select the formatting used for individual\nstruct fields by providing a format string in the style expected by Rust string\nformatting macros like `format!` and `println!`.\n\n```rust\nuse derive_debug::CustomDebug;\n\n#[derive(CustomDebug)]\npub struct Field {\n    name: String,\n    #[debug = \"0b{:08b}\"]\n    bitmask: u8,\n}\n```\n\nHere, one possible instance of the struct above might be printed by its\ngenerated `Debug` impl like this:\n\n```console\nField { name: \"st0\", bitmask: 0b00011100 }\n```\n\nThis project covers:\n\n- traversing syntax trees;\n- constructing output source code;\n- processing helper attributes;\n- dealing with lifetime parameters and type parameters;\n- inferring trait bounds on generic parameters of trait impls;\n- limitations of derive's ability to emit universally correct trait bounds.\n\n*Project skeleton is located under the \u003Ckbd>debug\u003C\u002Fkbd> directory.*\n\n### Function-like macro: `seq!`\n\nThis macro provides a syntax for stamping out sequentially indexed copies of an\narbitrary chunk of code.\n\nFor example our application may require an enum with sequentially numbered\nvariants like `Cpu0` `Cpu1` `Cpu2` ... `Cpu511`. But note that the same `seq!`\nmacro should work for any sort of compile-time loop; there is nothing specific\nto emitting enum variants. A different caller might use it for generating an\nexpression like `tuple.0 + tuple.1 + ... + tuple.511`.\n\n```rust\nuse seq::seq;\n\nseq!(N in 0..512 {\n    #[derive(Copy, Clone, PartialEq, Debug)]\n    pub enum Processor {\n        #(\n            Cpu~N,\n        )*\n    }\n});\n\nfn main() {\n    let cpu = Processor::Cpu8;\n\n    assert_eq!(cpu as u8, 8);\n    assert_eq!(cpu, Processor::Cpu8);\n}\n```\n\nThis project covers:\n\n- parsing custom syntax;\n- low-level representation of token streams;\n- constructing output source code.\n\n*Project skeleton is located under the \u003Ckbd>seq\u003C\u002Fkbd> directory.*\n\n### Attribute macro: `#[sorted]`\n\nA macro for when your coworkers (or you yourself) cannot seem to keep enum\nvariants in sorted order when adding variants or refactoring. The macro will\ndetect unsorted variants at compile time and emit an error pointing out which\nvariants are out of order.\n\n```rust\n#[sorted]\n#[derive(Debug)]\npub enum Error {\n    BlockSignal(signal::Error),\n    CreateCrasClient(libcras::Error),\n    CreateEventFd(sys_util::Error),\n    CreateSignalFd(sys_util::SignalFdError),\n    CreateSocket(io::Error),\n    DetectImageType(qcow::Error),\n    DeviceJail(io_jail::Error),\n    NetDeviceNew(virtio::NetError),\n    SpawnVcpu(io::Error),\n}\n```\n\nThis project covers:\n\n- compile-time error reporting;\n- application of visitor pattern to traverse a syntax tree;\n- limitations of the currently stable macro API and some ways to work around\n  them.\n\n*Project skeleton is located under the \u003Ckbd>sorted\u003C\u002Fkbd> directory.*\n\n### Attribute macro: `#[bitfield]`\n\nThis macro provides a mechanism for defining structs in a packed binary\nrepresentation with access to ranges of bits, similar to the language-level\nsupport for [bit fields in C].\n\n[bit fields in C]: https:\u002F\u002Fen.cppreference.com\u002Fw\u002Fcpp\u002Flanguage\u002Fbit_field\n\nThe macro will conceptualize one of these structs as a sequence of bits 0..N.\nThe bits are grouped into fields in the order specified by a struct written by\nthe caller. The `#[bitfield]` attribute rewrites the caller's struct into a\nprivate byte array representation with public getter and setter methods for each\nfield.\n\nThe total number of bits N is required to be a multiple of 8 (this will be\nchecked at compile time).\n\nFor example, the following invocation builds a struct with a total size of 32\nbits or 4 bytes. It places field `a` in the least significant bit of the first\nbyte, field `b` in the next three least significant bits, field `c` in the\nremaining four most significant bits of the first byte, and field `d` spanning\nthe next three bytes.\n\n```rust\nuse bitfield::*;\n\n#[bitfield]\npub struct MyFourBytes {\n    a: B1,\n    b: B3,\n    c: B4,\n    d: B24,\n}\n```\n\n```text\n                               least significant bit of third byte\n                                 ┊           most significant\n                                 ┊             ┊\n                                 ┊             ┊\n║  first byte   ║  second byte  ║  third byte   ║  fourth byte  ║\n╟───────────────╫───────────────╫───────────────╫───────────────╢\n║▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒ ▒║\n╟─╫─────╫───────╫───────────────────────────────────────────────╢\n║a║  b  ║   c   ║                       d                       ║\n                 ┊                                             ┊\n                 ┊                                             ┊\n               least significant bit of d         most significant\n```\n\nThe code emitted by the `#[bitfield]` macro for this struct would be as follows.\nNote that the field getters and setters use whichever of `u8`, `u16`, `u32`,\n`u64` is the smallest while being at least as large as the number of bits in\nthe field.\n\n```rust\nimpl MyFourBytes {\n    \u002F\u002F Initializes all fields to 0.\n    pub fn new() -> Self;\n\n    \u002F\u002F Field getters and setters:\n    pub fn get_a(&self) -> u8;\n    pub fn set_a(&mut self, val: u8);\n    pub fn get_b(&self) -> u8;\n    pub fn set_b(&mut self, val: u8);\n    pub fn get_c(&self) -> u8;\n    pub fn set_c(&mut self, val: u8);\n    pub fn get_d(&self) -> u32;\n    pub fn set_d(&mut self, val: u32);\n}\n```\n\nThis project covers:\n\n- traversing syntax trees;\n- processing helper attributes;\n- constructing output source code;\n- interacting with traits and structs other than from the standard library;\n- techniques for compile-time assertions that require type information, by\n  leveraging the trait system in interesting ways from generated code;\n- tricky code.\n\n*Project skeleton is located under the \u003Ckbd>bitfield\u003C\u002Fkbd> directory.*\n\n### Project recommendations\n\nIf this is your first time working with procedural macros, I would recommend\nstarting with the `derive(Builder)` project. This will get you comfortable with\ntraversing syntax trees and constructing output source code. These are the two\nfundamental components of a procedural macro.\n\nAfter that, it would be equally reasonable to jump to any of\n`derive(CustomDebug)`, `seq!`, or `#[sorted]`.\n\n- Go for `derive(CustomDebug)` if you are interested in exploring how macros\n  manipulate trait bounds, which is one of the most complicated aspects of\n  code generation in Rust involving generic code like [Serde]. This project\n  provides an approachable introduction to trait bounds and digs into many of\n  the challenging aspects.\n\n- Go for `seq!` if you are interested in parsing a custom input syntax yourself.\n  The other projects will all mostly rely on parsers that have already been\n  written and distributed as a library, since their input is ordinary Rust\n  syntax.\n\n- Go for `#[sorted]` if you are interested in generating diagnostics (custom\n  errors) via a macro. Part of this project also covers a different way of\n  processing input syntax trees; the other projects will do most things through\n  `if let`. The visitor approach is better suited to certain types of macros\n  involving statements or expressions as we'll see here when checking that\n  `match` arms are sorted.\n\n[Serde]: https:\u002F\u002Fserde.rs\u002F\n\nI would recommend starting on `#[bitfield]` only after you feel you have a\nstrong grasp on at least two of the other projects. Note that completing the\nfull intended design will involve writing at least one of all three types of\nprocedural macros and substantially more code than the other projects.\n\n\u003Cbr>\n\n## Test harness\n\nTesting macros thoroughly tends to be tricky. Rust and Cargo have a built-in\ntesting framework via `cargo test` which can work for testing the success cases,\nbut we also really care that our macros produce good error message when they\ndetect a problem at compile time; Cargo isn't able to say that failing to\ncompile is considered a success, and isn't able to compare that the error\nmessage produced by the compiler is exactly what we expect.\n\nThe project skeletons in this repository use an alternative test harness called\n[trybuild].\n\n[trybuild]: https:\u002F\u002Fgithub.com\u002Fdtolnay\u002Ftrybuild\n\n\u003Cp align=\"center\">\n\u003Ca href=\"#test-harness\">\n\u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F1940490\u002F55197640-eb390080-5191-11e9-8c1f-1183935c0c26.png\" width=\"600\">\n\u003C\u002Fa>\n\u003C\u002Fp>\n\nThe test harness is geared toward iterating on the implementation of a\nprocedural macro, observing the errors emitted by failed executions of the\nmacro, and testing that those errors are as expected.\n\n\u003Cbr>\n\n## Workflow\n\nEvery project has a test suite already written under its \u003Ckbd>tests\u003C\u002Fkbd>\ndirectory. (But feel free to add more tests, remove tests for functionality you\ndon't want to implement, or modify tests as you see fit to align with your\nimplementation.)\n\nRun `cargo test` inside any of the 5 top-level project directories to run the\ntest suite for that project.\n\nInitially every projects starts with all of its tests disabled. Open up the\nproject's *tests\u002Fprogress.rs* file and enable tests one at a time as you work\nthrough the implementation. **The test files (for example *tests\u002F01-parse.rs*)\neach contain a comment explaining what functionality is tested and giving some\ntips for how to implement it.** I recommend working through tests in numbered\norder, each time enabling one more test and getting it passing before moving on.\n\nTests come in two flavors: tests that should compile+run successfully, and tests\nthat should fail to compile with a specific error message.\n\nIf a test should compile and run successfully, but fails, the test runner will\nsurface the compiler error or runtime error output.\n\n\u003Cp align=\"center\">\n\u003Ca href=\"#workflow\">\n\u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F1940490\u002F55197637-eb390080-5191-11e9-9197-5832071639ea.png\" width=\"600\">\n\u003C\u002Fa>\n\u003C\u002Fp>\n\nFor tests that should fail to compile, we compare the compilation output against\na file of expected errors for that test. If those errors match, the test is\nconsidered to pass. If they do not match, the test runner will surface the\nexpected and actual output.\n\nExpected output goes in a file with the same name as the test except with an\nextension of _*.stderr_ instead of _*.rs_.\n\n\u003Cp align=\"center\">\n\u003Ca href=\"#workflow\">\n\u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F1940490\u002F55197639-eb390080-5191-11e9-9c8f-a47cab89652d.png\" width=\"600\">\n\u003C\u002Fa>\n\u003C\u002Fp>\n\nIf there is no _*.stderr_ file for a test that is supposed to fail to compile,\nthe test runner will save the compiler's output into a directory called\n\u003Ckbd>wip\u003C\u002Fkbd> adjacent to the \u003Ckbd>tests\u003C\u002Fkbd> directory. So the way to update\nthe \"expected\" output is to delete the existing _*.stderr_ file, run the tests\nagain so that the output is written to *wip*, and then move the new output from\n*wip* to *tests*.\n\n\u003Cp align=\"center\">\n\u003Ca href=\"#workflow\">\n\u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F1940490\u002F55197642-ebd19700-5191-11e9-8f00-2d7c5f4be1a9.png\" width=\"600\">\n\u003C\u002Fa>\n\u003C\u002Fp>\n\n\u003Cbr>\n\n## Debugging tips\n\nTo look at what code a macro is expanding into, install the [cargo expand] Cargo\nsubcommand and then run `cargo expand` in the repository root (outside of any of\nthe project directories) to expand the main.rs file in that directory. You can\ncopy any of the test cases into this main.rs and tweak it as you iterate on the\nmacro.\n\n[cargo expand]: https:\u002F\u002Fgithub.com\u002Fdtolnay\u002Fcargo-expand\n\nIf a macro is emitting syntactically invalid code (not just code that fails\ntype-checking) then cargo expand will not be able to show it. Instead have the\nmacro print its generated TokenStream to stderr before returning the tokens.\n\n```rust\neprintln!(\"TOKENS: {}\", tokens);\n```\n\nThen a `cargo check` in the repository root (if you are iterating using main.rs)\nor `cargo test` in the corresponding project directory will display this output\nduring macro expansion.\n\nStderr is also a helpful way to see the structure of the syntax tree that gets\nparsed from the input of the macro.\n\n```rust\neprintln!(\"INPUT: {:#?}\", syntax_tree);\n```\n\nNote that in order for Syn's syntax tree types to provide Debug impls, you will\nneed to set `features = [\"extra-traits\"]` on the dependency on Syn. This is\nbecause adding hundreds of Debug impls adds an appreciable amount of compile\ntime to Syn, and we really only need this enabled while doing development on a\nmacro rather than when the finished macro is published to users.\n\n\u003Cbr>\n\n### License\n\n\u003Csup>\nLicensed under either of \u003Ca href=\"LICENSE-APACHE\">Apache License, Version\n2.0\u003C\u002Fa> or \u003Ca href=\"LICENSE-MIT\">MIT license\u003C\u002Fa> at your option.\n\u003C\u002Fsup>\n\n\u003Cbr>\n\n\u003Csub>\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in this codebase by you, as defined in the Apache-2.0 license,\nshall be dual licensed as above, without any additional terms or conditions.\n\u003C\u002Fsub>\n","该项目旨在教授如何编写 Rust 过程宏，即用 Rust 代码生成 Rust 代码的技术。核心功能包括属性宏、派生宏和函数式宏的学习与实践，每个项目都基于实际应用场景设计，例如`derive(Builder)`用于实现构建者模式，`#[sorted]`属性宏则可以自动排序代码中的元素等。适合已经掌握Rust基本语法如结构体、枚举、trait等概念，并希望进一步深入理解过程宏机制的开发者使用。通过本项目，学习者能够动手实践并加深对Rust高级特性的理解。","2026-06-11 03:04:57","top_language"]