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. 自己紹介 • cipepser(さいぺ) • リードエンジニア@LayerX ◦ R&Dチーム • 経歴 ◦

    SIerで証券系システムのネットワーク設計、構築 ◦ Fintech会社で新規事業の立ち上げ • 趣味プロジェクト ◦ コンパイラ ◦ 正規表現ジェネレータ ◦ protocol buffersデコーダ ◦ (多くがやりかけ...)
  2. Procedural macrosをざっくりと理解する • Rust edition 2018から使えるようになった • 通常のRustマクロ ◦ macro_rules!

    ◦ パターンマッチ • Procedural macros ◦ Function-like macros ▪ custom!(...) ◦ Derive macros ▪ #[derive(CustomDerive)] ◦ Attribute macros ▪ #[CustomAttribute]
  3. 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]:
  4. proc_macro • Procedural macrosの実装をサポートするラ イブラリ • Cargo.tomlのlibで有効化して使う • proc_macro2というwrapperもある ◦

    build.rsやmain.rsのような別のコンテキストに proc_macroの機能を持ち込みたいときに使う • TokenStreamを操作する ◦ ASTを直接操作するわけではない ◦ Vec<TokenTree>をcloneしやすくしたもの ◦ TokenTree = lexical token https://doc.rust-lang.org/proc_macro/
  5. 例)synで関数名を取得する pub struct ItemFn { pub attrs: Vec<Attribute>, pub vis:

    Visibility, pub sig: Signature, pub block: Box<Block>, } pub struct Signature { pub constness: Option<Token![const]>, pub asyncness: Option<Token![async]>, pub unsafety: Option<Token![unsafe]>, pub abi: Option<Abi>, pub fn_token: Token![fn], pub ident: Ident, pub generics: Generics, pub paren_token: token::Paren, pub inputs: Punctuated<FnArg, Token![,]>, pub variadic: Option<Variadic>, pub output: ReturnType, } 関数名を持つ
  6. Attributeの復習 #[test] fn check() { let x = 1; assert_eq!(x,

    1); } #[test]を付与することで、check()に 「cargo test時に実行される」という意味を付与
  7. やること: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
  8. ディレクトリ構造とCargo.toml [lib] proc-macro = true [dependencies] proc-macro2 = "1.0" quote

    = "1.0" syn = "1.0" main関数を実装 標準出力の調整など Procedural macrosを実装 proc-macroを有効化
  9. #[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())が展開される
  10. 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<String> = Vec::new(); for t in inventory::iter::<utils::TestCase>.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を実行する
  11. 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関数内でテストを実行
  12. • Procedural macrosと通常のマクロを紹介 • Procedural macrosでよく利用される3つマクロたち ◦ proc_macroで、Procedural macrosが有効になる ◦

    synで、文字列からRustコードに構文解析する ◦ quoteで、synのデータ構造からRustコードに変換し直す • Custom Attributeを実装した ◦ 自分でも動かしたい!という方は以下にソースコードを公開してます。 ◦ https://github.com/cipepser/rust-custom-attribute まとめ
  13. 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