syn + quote

=

rust + Ada

History of ada

  • In the 70s, the USDoD was using over 400 programming languages
  • This was probably too many
  • Designed from 1977 to 1980
  • First implemented completely in 1983
  • Standardized in 1987, updated in 1995 and 2012
-- Ada
type My_Integer is new Integer;
type My_Integer is new Long_Integer;
type My_Integer is new Integer_32;
type My_Integer is new Integer range 10..9_999;
type My_Integer is range 10..9_999;

Foo: My_Integer;
Bar: Not_My_Integer;
Qux: Integer;

Foo := Foo + 1;  -- OK
Foo := Foo + Bar;  -- Not OK!
Foo := Foo + Qux;  -- Not OK!
Foo := Foo + Foo(Qux);  -- OK
// Rust
struct MyInteger(i32);
// below are hypothetical!
struct MyInteger(impl Int);
struct MyInteger(i32<10..10_000>);
struct MyInteger(impl Int<10..10_000>);

let foo: MyInteger;
let bar: NotMyInteger;
let qux: i32;

foo += 1;  // OK?
foo += bar;  // Not OK
foo += qux;  // OK?
foo += qux.into();  // OK

ada vs rust

  • Inheritance vs Composition
  • Exceptions vs Result
  • Dynamic checks vs Static Checks

syn & quote

  • Libraries for turning Rust tokens into an abstract syntax tree, and vice versa
  • From dtolnay, maintainer of Serde
  • Used by Serde and every other proc macro
  • Without them, developing proc macros would be very, very painful
  • Seriously don't even try it

Proc Macros

  • Rust's secret-est weapon
  • Run arbitrary Rust code at compile-time to replace one token tree with any other token tree
  • The "heavyweight" alternative to Rust's "lightweight" declarative macros
  • Serde, Diesel, and Rocket all rely on this

what even is a token tree

  • Classical compiler architecture is described as a pipeline:                                                               Lexing Parsing Analysis Optimization Machine Code Generation
  • Token trees are the intermediate step between lexing and parsing
  • We can take as input any Rust code that passes the lexer, and output any Rust code
// Rust + procedural macros
struct MyInteger(i32);

#[range(10..10_000)]
struct MyInteger(i32);

#[range(10..10_000)]
struct MyInteger(impl Int);

#[range(1..10, 91..100)]
struct MyInteger(i32);

my_int += 1;  // OK
my_int += not_a_my_int;  // Not OK
my_int += some_i32;  // OK???
my_int += some_i32.into();  // OK
Made with Slides.com