Upgrade to Pro — share decks privately, control downloads, hide ads and more …

procedural-macros

 procedural-macros

cipepser

June 16, 2020
Tweet

More Decks by cipepser

Other Decks in Technology

Transcript

  1. Procedural macros入門
    cipepser
    2020/6/16
    下町.rs #2

    View Slide

  2. 自己紹介
    ● cipepser(さいぺ)
    ● リードエンジニア@LayerX
    ○ R&Dチーム
    ● 経歴
    ○ SIerで証券系システムのネットワーク設計、構築
    ○ Fintech会社で新規事業の立ち上げ
    ● 趣味プロジェクト
    ○ コンパイラ
    ○ 正規表現ジェネレータ
    ○ protocol buffersデコーダ
    ○ (多くがやりかけ...)

    View Slide

  3. Anonifyをリリースしました
    ソースコード
    ホワイトペーパー
    https://github.com/LayerXcom/anonify
    https://layerx.co.jp/wp-content/uploads/2020/06/anonify.pdf
    ドキュメント
    https://layerxcom.github.io/anonify-book/
    スライド
    https://speakerdeck.com/layerx/anonify
    TEE内のテストにProcedural macrosを利用している

    View Slide

  4. アジェンダ
    1. Procedural macros概要
    2. 通常のRustマクロ
    3. Procedural macrosでよく利用される3つマクロたち
    4. Custom Attributeの実装

    View Slide

  5. Procedural macros概要

    View Slide

  6. Procedural macrosをざっくりと理解する
    ● Rust edition 2018から使えるようになった
    ● 通常のRustマクロ
    ○ macro_rules!
    ○ パターンマッチ
    ● Procedural macros
    ○ Function-like macros
    ■ custom!(...)
    ○ Derive macros
    ■ #[derive(CustomDerive)]
    ○ Attribute macros
    ■ #[CustomAttribute]

    View Slide

  7. 通常のRustマクロ

    View Slide

  8. macro_rules! vec {
    () => (
    $crate::vec::Vec::new()
    );
    ($elem:expr; $n:expr) => (
    $crate::vec::from_elem($elem, $n)
    );
    ($($x:expr),+ $(,)?) => (
    ::into_vec(box [$($x),+])
    );
    }
    Proceduralでない通常のRustマクロはパターンマッチがベース
    https://github.com/rust-lang/rust/blob/master/src/liballoc/macros.rs
    ● (pattern) => (template);
    ● *, +による繰り返し
    ○ 参照は$で対になっている必要がある
    ● exprやtyなどのフラグメント型
    vec![]:
    vec![“a”; 3]:
    vec![1,2,3]:

    View Slide

  9. Procedural macrosでよく利用される
    3つマクロたち

    View Slide

  10. proc_macro
    ● Procedural macrosの実装をサポートするラ
    イブラリ
    ● Cargo.tomlのlibで有効化して使う
    ● proc_macro2というwrapperもある
    ○ build.rsやmain.rsのような別のコンテキストに
    proc_macroの機能を持ち込みたいときに使う
    ● TokenStreamを操作する
    ○ ASTを直接操作するわけではない
    ○ Vecをcloneしやすくしたもの
    ○ TokenTree = lexical token
    https://doc.rust-lang.org/proc_macro/

    View Slide

  11. syn
    ● 文字列を入力に構文解析
    ● validなRustコードであることを保証
    ● Structsでもろもろ定義されている
    ○ 次ページでItemFnを例にみてみます
    https://docs.rs/syn/1.0.31/syn/

    View Slide

  12. 例)synで関数名を取得する
    pub struct ItemFn {
    pub attrs: Vec,
    pub vis: Visibility,
    pub sig: Signature,
    pub block: Box,
    }
    pub struct Signature {
    pub constness: Option,
    pub asyncness: Option,
    pub unsafety: Option,
    pub abi: Option,
    pub fn_token: Token![fn],
    pub ident: Ident,
    pub generics: Generics,
    pub paren_token: token::Paren,
    pub inputs: Punctuated,
    pub variadic: Option,
    pub output: ReturnType,
    }
    関数名を持つ

    View Slide

  13. quote
    ● synのデータ構造を受け取り、トークン列に
    変換し直す
    ● #varで補完
    ○ 例)取得した関数名を利用したいとき
    https://docs.rs/quote/1.0.7/quote/
    let f = parse_macro_input!(input as ItemFn);
    let f_ident = &f.sig.ident;
    let q = quote!(
    #f_ident
    ..
    };

    View Slide

  14. Custom Attributeの実装

    View Slide

  15. Attributeの復習
    #[test]
    fn check() {
    let x = 1;
    assert_eq!(x, 1);
    }
    #[test]を付与することで、check()に
    「cargo test時に実行される」という意味を付与

    View Slide

  16. やること:Custom Attributeで、cargo runでテストを走らせます
    use utils::{test_case, run_inventory_tests};
    #[test_case]
    fn check() {
    let x = 1;
    assert_eq!(x, 1);
    }
    #[test_case]
    fn fail() {
    let x = 1;
    assert_eq!(x, 2);
    }
    fn main() {
    run_inventory_tests!();
    }
    ● #[test_case] attributeを自作
    ● cargo runで実行
    ○ ✖ cargo test

    View Slide

  17. ディレクトリ構造とCargo.toml
    [lib]
    proc-macro = true
    [dependencies]
    proc-macro2 = "1.0"
    quote = "1.0"
    syn = "1.0"
    main関数を実装
    標準出力の調整など
    Procedural macrosを実装
    proc-macroを有効化

    View Slide

  18. #[proc_macro_attribute]
    pub fn test_case(_attr: TokenStream, input: TokenStream) -> TokenStream {
    let f = parse_macro_input!(input as ItemFn);
    let f_ident = &f.sig.ident;
    let q = quote!(
    #f
    inventory::submit!(
    utils::TestCase(
    concat!(module_path!(), "::", stringify!(#f_ident)).to_string(),
    #f_ident
    )
    );
    );
    q.into()
    }
    Attributeを実装する
    TokenStreamをItemFnとしてparse
    失敗したらコンパイルエラー
    attributeの名前を関数名にする
    関数(check()やfail())が展開される

    View Slide

  19. pub struct TestCase(pub String, pub fn() -> ());
    inventory::collect!(TestCase);
    #[macro_export]
    macro_rules! run_inventory_tests {
    () => {
    utils::test_start();
    let mut ntestcases: u64 = 0u64;
    let mut failurecases: Vec = Vec::new();
    for t in inventory::iter::.into_iter() {
    utils::test(&mut ntestcases, &mut failurecases, t.1, &t.0);
    }
    utils::test_end(ntestcases, failurecases)
    };
    }
    #[test_case] attrを付与した関数を集める
    submit!した関数たちを集めてくる
    utils::test内でt.0を実行する

    View Slide

  20. custom attributesを使ってみる
    use utils::{test_case, run_inventory_tests};
    #[test_case]
    fn check() {
    let x = 1;
    assert_eq!(x, 1);
    }
    #[test_case]
    fn fail() {
    let x = 1;
    assert_eq!(x, 2);
    }
    fn main() {
    run_inventory_tests!();
    }
    attrを設定
    attrを設定
    main関数内でテストを実行

    View Slide

  21. `cargo test`ではなく、`cargo run`でテストを実行する
    関数名(checkやfail)も出力できた

    View Slide

  22. ● Procedural macrosと通常のマクロを紹介
    ● Procedural macrosでよく利用される3つマクロたち
    ○ proc_macroで、Procedural macrosが有効になる
    ○ synで、文字列からRustコードに構文解析する
    ○ quoteで、synのデータ構造からRustコードに変換し直す
    ● Custom Attributeを実装した
    ○ 自分でも動かしたい!という方は以下にソースコードを公開してます。
    ○ https://github.com/cipepser/rust-custom-attribute
    まとめ

    View Slide

  23. References
    ● proc_macro - Rust https://doc.rust-lang.org/proc_macro/
    ● O'Reilly Japan - プログラミングRust https://www.oreilly.co.jp/books/9784873118550/
    ● Procedural Macros - The Rust Reference https://doc.rust-lang.org/reference/procedural-macros.html
    ● D - マクロ - The Rust Programming Language https://doc.rust-jp.rs/book/second-edition/appendix-04-macros.html
    ● proc_macro2 - Rust https://docs.rs/proc-macro2/1.0.18/proc_macro2/
    ● syn - Rust https://docs.rs/syn/1.0.31/syn/
    ● quote - Rust https://docs.rs/quote/1.0.7/quote/
    ● inventory - Rust https://docs.rs/inventory/0.1.6/inventory/
    ● Procedural Macros に入門していたずらしてみた - Don't Repeat Yourself https://yuk1tyd.hatenablog.com/entry/2018/12/25/192041
    ● Procedural macros 雑入門 - slideship.com
    https://slideship.com/users/@statiolake/presentations/2019/07/CZ9wX4Zi8R93MhxN7jRBK5/?p=2

    View Slide

  24. ブロックチェーン技術をもとに、「新たな経済基盤」をつくりだす。
    それは、信用や評価のあり方を変え、
    業務や生産をはじめとした経済活動の摩擦を解消し、
    この国の課題である生産性向上を実現する。
    私たちは、そう信じて行動し続けます。
    ブロックチェーンが実装された社会、
    そこには、これまでの延⻑にはないまったく新しい可能性が広がっている。
    LayerXは、デジタル社会への発展を後押しすることで、
    経済史に新たな1ページを刻んでいきます。

    View Slide