1 of 32

My Little Procedural Macro

Chris Wong

2 of 32

What is a procedural macro?

3 of 32

A procedural macro extends the syntax of Rust

4 of 32

Parser

Source Code

Binary

* Type checking, optimization, etc.

MAGIC*

Procedural macro

5 of 32

Procedural macros are everywhere!

  • Serde (serialization)
  • Diesel (databases)
  • Rocket (web framework)
  • ...
  • Maud

6 of 32

What is maud?

7 of 32

Maud is a macro for writing HTML

8 of 32

A macro for writing HTML

use maud::html;�let best_pony = "Pinkie Pie";�let page = html! {� p {� "Hello, " (best_pony)� }�};

println!("{}", page.into_string());

9 of 32

A macro for writing HTML

$ cargo run

<p>Hello, Pinkie Pie</p>

10 of 32

Low overhead

{

let __output = String::new();

__output.push_str("<p>Hello, ");

best_pony.render_to(&mut output);

__output.push_str("</p>");

::maud::PreEscaped(__output)

}

11 of 32

12 of 32

Static type checking

use maud::html;�let best_pony = "Pinkie Pie";�let page = html! {� p {� "Hello, " (best_pone)� }�};

println!("{}", page.into_string());

13 of 32

Static type checking

error[E0425]: cannot find value `best_pone` in this scope

--> maud_test/src/main.rs:5:20

|

5 | "Hello, " (best_pone)

| ^^^^^^^^^ did you mean `best_pony`?

14 of 32

How it all started

15 of 32

16 of 32

Hamlet (Haskell)

Slim (Ruby)

Razor (C#)

??? (Rust)

17 of 32

How to write a successful Rust library

  1. Take an existing idea from another language
  2. Rewrite it in Rust
  3. Profit!

18 of 32

Developing on Rust Nightly

19 of 32

Bleeding edge!

  • Maud 0.1 was released Jan 2015
  • Rust 1.0 was released May 2015
  • Proc macro API was (and for the most part still is) unstable
  • The build still breaks every month (was: every few days)

20 of 32

Rolling with the punches

  • Schedule a daily build using Travis CI
  • Research Rust commit history, blame, PRs
  • Subscribe to relevant tracking issues

21 of 32

Keeping it simple?

22 of 32

Don’t go crazy with the syntax

  • Put distinguishing markers at the beginning
    • e.g. the let statement in Rust
  • Use delimiters () [] {} as much as possible
    • Rust parses these automatically

23 of 32

^foo.bar.baz

{{ foo.bar.baz }}

(foo.bar.baz)

https://www.deviantart.com/dipi11/art/Pinkie-Pie-in-Thought-421240604

https://www.deviantart.com/luckreza8/art/Mlp-Fim-pinkie-pie-how-do-you-think-vector-573248347

24 of 32

Don’t fear the heap

  • Box, String, Vec aren’t evil
  • Zero-allocation code can be hard to read
  • Sometimes, allocations can make code faster!

25 of 32

fn article<'r>(

title: &'r str,

content: &'r str,

) -> impl RenderOnce + 'r {

// ...

}

https://www.deviantart.com/spyro4287/art/Pinkie-Pie-Scared-544318678

26 of 32

Implementation details

27 of 32

Use an abstract syntax tree (AST)

Even if you think you don’t need it

You’ll thank yourself later

28 of 32

Keep the spans

  • These tell the compiler where a syntax fragment came from
  • This improves compiler errors
  • Also avoids weird scoping issues

29 of 32

error: duplicate attribute `href`

--> maud/tests/basic_syntax.rs:13:15

|

13 | html! { a href="foo.html" href="bar.html" {} }

| ^^^^^^^^^^^^^^^

|

note: `href` is duplicated here

--> maud/tests/basic_syntax.rs:13:31

|

13 | html! { a href="foo.html" href="bar.html" {} }

| ^^^^^^^^^^^^^^^

30 of 32

Where to next?

31 of 32

Where to next?

  • More diagnostics!
    • Invalid/non-standard elements and attributes
    • rel="noopener"
  • Work on stable Rust?

32 of 32

Thanks for listening!

https://github.com/lfairy/maud