Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]()
33 changes: 20 additions & 13 deletions src/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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 <i class="fas fa-play"></i> 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 <i class="fas fa-play"></i> 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 <i class="fas fa-play"></i> button to verify your changes compile and behave as expected
- Don't hesitate to break things - the tutorial is designed for experimentation
97 changes: 97 additions & 0 deletions src/macros_1.0/metavariables.md
Original file line number Diff line number Diff line change
@@ -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<String>`, `&'a str` |
| `vis` | A visibility qualifier (this can be empty). | `pub`, `pub(crate)` |
1 change: 1 addition & 0 deletions src/macros_1.0_reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Macros 1.0 macro_rules!