+ - 0:00:00
Notes for current slide
Notes for next slide

Makra v Rustu

Makra v Rustu

Michal Vaner (vorner@vorner.cz)

1 / 17

Makra v Rustu

Představení

2 / 17

Makra v Rustu

Proč makra

  • Declarative macros
  • Opakující se kód
    • A nelze použít jiná metoda
  • DSL
bitflags! {
struct Flags: u32 {
const A = 0b00000001;
const B = 0b00000010;
const C = 0b00000100;
const ABC = Self::A.bits | Self::B.bits | Self::C.bits;
}
}
3 / 17

Makra v Rustu

Ukázka opakujícího se kódu

#[derive(Copy, Clone, Debug)]
struct Point(f64, f64);
impl Add for Point {
type Output = Self;
fn add(self, other: Self) -> Self {
Self (self.0 + other.0, self.1 + other.1)
}
}
impl Sub for Point {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self (self.0 - other.0, self.1 - other.1)
}
}
4 / 17

Makra v Rustu

Alternativy

  • Generika (pomocí traitů)
    • Musí sedět typy
  • build.rs
    • Neumí „reagovat“ na vstup
  • Procedurální makra
    • Samostatný crate
    • Možná libovolná manipulace
    • I derive a attribute makra
    • Obvykle pomocí quote a syn
    • Velmi flexibilní, ale pracné
5 / 17

Makra v Rustu

build.rs

fn main() -> Result<(), Error> {
fs::write(
format!("{}/x.rs", env::var("OUT_DIR").unwrap()),
r#"
fn hello() -> usize {
42
}
"#,
)?;
Ok(())
}
include!(concat!(env!("OUT_DIR"), "/x.rs"));
6 / 17

Makra v Rustu

Procedurální makra

#[proc_macro_derive(MyTrait)]
pub fn my_derive(input: TokenStream) -> TokenStream {
let input: DeriveInput = syn::parse(input).unwrap();
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) =
input.generics.split_for_impl();
...
quote!(
impl #impl_generics MyTrait for #name #ty_generics {
...
}
).into()
}
7 / 17

Makra v Rustu

Použití

  • Volání s vykřičníkem
  • Libovolné závorky (ale párové)
  • Nahradí za "rozbalení" makra
  • Pracuje nad syntaktickými stromy
  • Hygienické
8 / 17

Makra v Rustu

Ukázka

fn main() {
println!("Hello world");
}
9 / 17

Makra v Rustu

Ukázka ‒ cargo expand

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
fn main() {
{
::std::io::_print(
::core::fmt::Arguments::new_v1(&["Hello, world!\n"], &[])
);
};
}
10 / 17

Makra v Rustu

Jak psát

  • Napřed napsat ručně
  • Poté "zmakrovat"
  • Základní anatomie
macro_rules! useless_plus {
(add $a: expr => $b: expr) => {
$a + $b
}
}
let x = useless_plus!(add 42 => 12);
11 / 17

Makra v Rustu

Metaproměnné a jejich typy

  • block: { ... }
  • expr: 1 + 2
  • ident: my_variable
  • item: fn hello() { ... }
  • meta: #[derive(...)]
  • tt: Cokoliv
  • ty: std::collections::LinkedList<usize>
  • ...
12 / 17

Makra v Rustu

Více patternů

macro_rules! mac {
($a: expr) => { ... };
($a: expr, $b: expr) => { ... };
}
13 / 17

Makra v Rustu

Opakování

  • Opakované části vzoru
  • Specifikátory *, + a ?
macro_rules! sum_many {
($($a: expr, )+) => {
[ $( &$a, )+ ].into_iter().sum()
}
}
14 / 17

Makra v Rustu

Rekurze

macro_rules! sum_many {
($($a: expr, )+) => {
[ $( &$a, )+ ].into_iter().sum()
};
($($a: expr),+) => {
sum_many!($( $a, )+)
};
}
15 / 17

Makra v Rustu

Použití z vnějšku

  • #[macro_export]
  • $crate
  • Plné cesty a reexporty
#[doc(hidden)]
pub mod reexports {
pub use std;
}
#[macro_export]
macro_rules boom {
() => { $crate::reexports::std::panic::panic!("Boom!") }
}
16 / 17

Makra v Rustu

Další zdroje

17 / 17

Makra v Rustu

Představení

2 / 17
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow