2021年03月16日 : 黒魔術に手を出してみる

といっても

Rustのマクロのことなんですけどね……。

プログラミング言語のマクロというと、私としてはC言語のそれしか比較相手知らないんですが、 Rustのマクロは、Cのマクロ関数とかと同じこともできるんですが、 闇の眷属が喜びそうな、もっとやばい感じのこともできるようで。

その一個が deriveマクロ なんですが、 これがどういうものかというと、標準で用意されているderiveマクロに、Debugというやつがいて、 たとえば

struct Hoge {
    fuga: i32,
    moge: i32
}

という構造体があった場合に、頭に

#[derive(Debug)]
struct Hoge {
    fuga: i32,
    moge: i32
}

と、 #[derive(Debug)] とつけるだけで、 自動で以下のようなコードを生成してくれるというすごいやつです。

impl fmt::Debug for Hoge {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Hoge")
         .field("fuga", &self.fuga)
         .field("moge", &self.hoge)
         .finish()
    }
}

ちなみに、上記が何をやってるかというと、 fmt::Debug っていうtrait(JavaでいうとこのInterface的なもの)の実装に必要なメソッドfmt() を生やしてくれて、 println!("{:?}", hoge) とかでデバッグ用の出力をすると

Hoge {fuga: 0, moge: 1}

みたく、構造体の内容を良い感じに表示してくれるようになるのです (付けないとエラーになりますが、逆に自分でもっと良さげな表示になるように fmt() を実装できる)。

もちろん、この Debug マクロくらい凝ったやつ作るのは大変そうなんですが、 パーサーは標準で用意されているので、

#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    let ast: syn::DeriveInput = syn::parse(input).unwrap();
    let name = &ast.ident;
    let gen = quote! {
        impl HelloMacro for #name {
            fn hello() {
                println!("Hello, Macro! My name is {}!", stringify!(#name));
            }
        }
    };
    gen.into()
}

なんて書いておくと、自分の構造体の名前を吐けるメソッドを自動で追加できたりするみたいです。 quote! マクロで囲まれてるやつが生成されるコードのテンプレート的なところですな。

ということで、練習で書いてる MOVコンテナの解析ツール を整理中……。 まだまだ綺麗に書けそうというか、こういうのはどういう風につくるのが良いのかなぁ……。 単純なパーサーなら、全部enumで良いのだろうけど、かなり巨大なデータ持ってるからなぁ……。

まぁ、ぼちぼちさわっていきます。

本日のタグ