diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 2dc4cf2..aaa0edf 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -2,14 +2,7 @@ [Introduction](./introduction.md) -# Tutorial - - [Macros 1.0: macro_rules!](./macros_1.0.md) + - [Metavariables](./macros_1.0/metavariables.md) - [Macros 2.0]() - [Procedural macros](./procedural_macros.md) - -# Reference - -- [Macros 1.0 `macro_rules!`]() -- [Macros 2.0]() -- [Procedural macros]() diff --git a/src/introduction.md b/src/introduction.md index 8ab0a97..980af3e 100644 --- a/src/introduction.md +++ b/src/introduction.md @@ -4,11 +4,11 @@ Welcome Rustacean! Let's learn about Rust macros. ## Prerequisites -To follow this tutorial, you should have: +To get the most out of this tutorial, we recommend having: -- Basic knowledge of Rust (variables, functions, structs, etc.) -- A working Rust project setup (using `cargo`) -- Understanding of Rust's type system and ownership +- **Familiarity with basic Rust syntax** (variables, functions, and structs). +- **A functional Rust development environment** (with `cargo` installed). +- **A solid understanding** of Rust's core concepts, such as the type system and ownership. If you're new to Rust, consider completing the official [Rust Book](https://doc.rust-lang.org/book/) first. @@ -36,21 +36,28 @@ Rust has two main kinds of macros: ## What You'll Learn In this tutorial, you will learn: -- How macros work under the hood - How to write your own macros - Best practices for macro safety and maintainability - Common macro patterns used in the Rust ecosystem ## How to Use This Book -It is recommended to read the "Tutorial" part in order. +This tutorial is designed to be interactive and hands-on. Here's how to get the most out of it: -This book has many runnable and editable code blocks. It is encouraged to run them by clicking the play button at the top-right of each code block. +### Reading Order -For example: +For the best learning experience, we recommend reading this tutorial in order. While you are free to explore different sections, macros build on concepts progressively; following the intended sequence will help you understand the material more effectively. -```rust,editable -fn main() { - println!("Hello, world!"); -} -``` +### Interactive Code Blocks + +This book contains many **runnable and editable** Rust code blocks: + +- **Runnable**: Click the play button at the top-right of any code block to execute it and see the output directly. +- **Editable**: Edit the code directly in the editor, then click the play button to re-run the updated code with your changes. + + +### Tips for Learning + +- Try modifying the examples to understand how macros work +- Use the button to verify your changes compile and behave as expected +- Don't hesitate to break things - the tutorial is designed for experimentation diff --git a/src/macros_1.0/metavariables.md b/src/macros_1.0/metavariables.md new file mode 100644 index 0000000..d05d27f --- /dev/null +++ b/src/macros_1.0/metavariables.md @@ -0,0 +1,97 @@ +# Metavariables + +Without metavariables, a rule can only match literal values exactly. + +## A First Look at Metavariables + +```rust,editable +macro_rules! print{ + ($x:tt) => { println!("{}", $x); }; +} +fn main() { + print!(1); + let v="Hello, world!"; + print!(v); +} +``` + +`$x:tt` is a metavariable named `x` with the type `tt` (token tree). + +> [!NOTE] +> You may find that other resources use the term "fragment specifier" to describe metavariable types. + +`tt` (Token Tree): Matches a single token or multiple tokens within matching delimiters `()`, `[]`, or `{}`. + +While there are many other metavariable types, the `tt` type is the most flexible. You can think of it as a "catch-all" metavariable type. + +## Building a DSL Example + +```rust,editable +macro_rules! SQL{ +// `expr` matches an expression. +(SELECT $e:expr;)=>{ + println!("Value: {}", $e); +}; + +// `ident` matches a single identifier. +(SELECT * FROM $t:ident)=>{ + println!("Table: {}", $t); +}; + +// Catch-all case. This uses repetition, which will be introduced in the next chapter. +($($t:tt)*) => { + print!("Unknown: "); + $( + print!("{} ", stringify!($t)); + )* + println!(); + }; +} +fn main(){ + SQL!(SELECT (3+2*4);); + let user_table="USER_TABLE"; + SQL!(SELECT * FROM user_table); + SQL!(SELECT 1+1 AS total FROM "USER_TABLE" WHERE id > 10); +} +``` + +> [!TIP] +> Always use the most specific metavariable type possible. For example, if you only need to match a single identifier, use `ident` instead of `tt`. + +## The Built-in `$crate` Metavariable + +`$crate` is a built-in metavariable that always expands to the name of the crate being compiled. + +```rust,editable +mod utils{ + pub const message: &'static str="Hello, world!"; +} +macro_rules! greet{ + () => { + println!("{}!", $crate::utils::message); + }; +} +fn main(){ + greet!(); +} +``` + +## Metavariable Types + +| Type | Description | Example | +| :--- | :--- | :--- | +| `block` | A block expression (code enclosed in braces `{}`). | `{ let x = 5; x }` | +| `expr` | A valid Rust expression. (In Rust 2024, this includes `_` and `const {}`). | `2 + 2`, `f(x)`, `const { 1 + 1 }` | +| `expr_2021` | An expression, but excludes `_` (UnderscoreExpression) and `const {}` (ConstBlockExpression). | `a * b`, `Some(42)` | +| `ident` | An identifier or a keyword. (Excludes `_`, raw identifiers like `r#foo`, and `$crate`). | `count`, `String`, `match` | +| `item` | A Rust item (such as a function, struct, trait, or module). | `fn run() {}`, `struct User { id: u32 }` | +| `lifetime` | A lifetime token. | `'a`, `'static` | +| `literal` | A literal value (optionally prefixed with a minus sign `-`). | `42`, `-10.5`, `"hello"`, `b'a'` | +| `meta` | The contents (the "meta item") found inside an attribute like `#[...]`. | `derive(Debug, Clone)`, `name = "value"` | +| `pat` | A pattern. Since Rust 2021, it allows top-level "or-patterns" (using `\|`). | `Some(x)`, `1..=5`, `_`, `0 \| 1` | +| `pat_param` | A pattern that does **not** allow top-level "or-patterns". | `Some(x)`, `42` | +| `path` | A path (TypePath style). | `std::io`, `self::utils::process` | +| `stmt` | A statement. Usually captures without the trailing semicolon. | `let x = 1`, `drop(y)` | +| `tt` | A **Token Tree**. It can be a single token or tokens inside matching `()`, `[]`, or `{}`. | `!`, `my_var`, `{ 1, 2, 3 }` | +| `ty` | A Rust type. | `i32`, `Vec`, `&'a str` | +| `vis` | A visibility qualifier (this can be empty). | `pub`, `pub(crate)` | diff --git a/src/macros_1.0_reference.md b/src/macros_1.0_reference.md new file mode 100644 index 0000000..8d3c6ad --- /dev/null +++ b/src/macros_1.0_reference.md @@ -0,0 +1 @@ +# Macros 1.0 macro_rules!