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
5 changes: 4 additions & 1 deletion src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@

- [Macros 1.0: macro_rules!](./macros_1.0.md)
- [Metavariables](./macros_1.0/metavariables.md)
- [Macros 2.0]()
- [Repetitions](./macros_1.0/repetitions.md)
- [Scoping](./macros_1.0/scoping.md)
- [Hygiene](./macros_1.0/hygiene.md)
- [Macros 2.0](./macros_2.0.md)
- [Procedural macros](./procedural_macros.md)
4 changes: 2 additions & 2 deletions src/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ Rust has two main kinds of macros:

## What You'll Learn

In this tutorial, you will learn:
Here's what you'll learn in this tutorial:
- How to write your own macros
- Best practices for macro safety and maintainability
- Common macro patterns used in the Rust ecosystem
- Common macro patterns in the Rust ecosystem

## How to Use This Book

Expand Down
26 changes: 26 additions & 0 deletions src/macros_1.0/hygiene.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Hygiene

```rust,editable

fn func() {
println!("Hello, world! (from definition site)");
}

fn main(){
let x = 1;
macro_rules! m {
() => {
println!("x = {}", x); // Uses `x` from the definition site.
func(); // Uses `func` from the invocation site.
$crate::func(); // meta-variable `$crate` refers to the crate where the macro is defined.
};
}
{
let x = 2;
fn func() {
println!("Hello, Rustacean! (from invocation site)");
}
m!();
}
}
```
71 changes: 71 additions & 0 deletions src/macros_1.0/repetitions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Repetitions

We use `$(<metavariable>)[delimiter]<*|?|+>` to specify metavariable repetition in both the pattern (matcher) and the expansion (transcriber).

- `*`: Zero or more times
- `?`: Zero or one time
- `+`: One or more times

## Optional Repetitions (?)

```rust,editable
macro_rules! greet{
($($msg:literal)?) => {
let msg = concat!("Hello, world! ", $($msg)?);
println!("{}", msg);
}
}

fn main(){
greet!();
greet!("Hello, Rustacean! 👋");
}
```

## Zero or More Repetitions (*)

```rust,editable
macro_rules! sum{
($($n:expr)*) => {
let mut sum=0;
$(sum += $n;)*
println!("The sum is {}", sum);
}
}
fn main(){
sum!(1 2 3);
}
```

## Repetitions with Delimiters

```rust,editable
macro_rules! sum{
($($n:expr),*) => {
let mut sum=0;
$(sum += $n;)*
println!("The sum is {}", sum);
}
}
fn main(){
sum!(1, 2, 3);
}
```

## Custom Delimiter Tokens

```rust,editable
macro_rules! sum{
($($n:literal)add*) => {
let mut sum=0;
$(sum += $n;)*
println!("The sum is {}", sum);
}
}
fn main(){
sum!(1 add 2 add 3);
}
```

> [!TIP]
> The delimiter token can be any token other than a delimiter or one of the repetition operators, but `;` and `,` are the most common.
81 changes: 81 additions & 0 deletions src/macros_1.0/scoping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Scoping

## Textual scope

```rust,editable
fn main(){
// m!{}; // Error: macro `m` is not defined in this scope

// new scope
{
macro_rules! m{
() => {
println!("Hello, world!");
}
}
m!{}; // OK

// shadowing
macro_rules! m{
() => {
println!("Hello, Rustacean!");
}
}
m!{}; // OK
}

// m!{}; // Error: macro `m` is not defined in this scope
}
```

> [!TIP]
> Textual scope works similarly to the scope of local variables declared with `let`.

## Path-based Scope

```rust,editable
mod mod1{
macro_rules! m{
() => {
println!("Hello, world!");
}
}
}

mod mod2{
macro_rules! m{
() => {
println!("Hello, Rustacean!");
}
}
pub(crate) use m; // re-export to gain path-based scope
}

fn main(){
// mod1::m!{}; // Error: By default, a macro has no path-based scope.
mod2::m!{}; // OK: `m` is re-exported from `mod2`.
}
```

## Exporting Macros

```rust,editable
mod mod_level_1{
mod mod_level_2{
// By default, a macro is implicitly `pub(crate)`
// `#[macro_export]` makes it `pub` and export it to the root of the crate.
#[macro_export]
macro_rules! m{
() => {
println!("Hello, world!");
}
}
}
}

fn main(){
// mod_level_1::m!{}; // Error: `m` is not in scope.
// mod_level_1::mod_level_2::m!{}; // Error: `m` is not in scope.
crate::m!{}; // OK
}
```
22 changes: 22 additions & 0 deletions src/macros_2.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Macros 2.0

[#39412](https://github.com/rust-lang/rust/issues/39412)

> [!NOTE]
> Macros 2.0 is a proposal to improve the macros 1.0 (declarative macros `macro_rules!`). It is not yet implemented.

## Why Macros 2.0?

Macros 1.0 has several limitations:

- Lack of hygiene: Macros 1.0 can capture variables from the surrounding scope, which can lead to unexpected behavior.
- Lack of modularity: Macros 1.0 cannot be easily composed or reused.
- Lack of type safety: Macros 1.0 cannot be easily checked for type safety.

Macros 2.0 aims to address these limitations by providing a more powerful and flexible macro system.

> [!NOTE]
> We will get it back when it is implemented.

> [!TIP]
> We actually don't need macros 1.0 or macros 2.0, if we can use procedural macros.