手続き的マクロやらcustom Derive周りを制御している周りの調査。 ほとんどソースファイルのコメントに書いていることなので新規性はない。
Rust は macro_rules! で定義する宣言的 (declarative) なマクロの他に、手続き的 (procedural) なマクロを使用することが出来る。これは、通常のRustコードを用いてマクロ定義を行うものであり、次の利点を持っている。
- 単純なパターンマッチでは記述の不可能なマクロを定義できる
- 具象構文を介することなく、抽象構文を直接構築することが出来る
asm!()などで用いられている- 参考: http://qnighy.hatenablog.com/entry/2017/04/12/070000
procedural macro では、次に挙げる3つの形式のマクロを定義することができる。
- Function (Bung) 形式
foo!(...)
- Attribute 形式
#[hoge(...)] mod foo;
- 元の要素を書き換えたり無視することが可能
#[cfg(...)]とか#[test]は多分これ
- 元の要素を書き換えたり無視することが可能
- Derive 形式
#[derive(...)] struct Hoge { #[foo(bar)] item: u32, }
- 元の要素を置き換えない(新たにコードを生成するだけ)
残念ながら本記事の執筆時の最新安定版である 1.21 ではすべての機能が安定化されておらず、custom Derive のみが stable チャンネルで使用可能になっている。procedural macro を定義するためには次の2つの方法を取る。
- compiler plugin を用いる
- すべての機能が使える
- nightly 限定
proc_macroを有効にしたクレートを用いる- stable/nightly双方で使用可能
- 一部の機能が unstable になっており、外部クレートで代用する必要がある
src/syntax/ext/base.rs で定義されている列挙体であり、以下のヴァリアントを持つ。
各項目には、ExtCtxt と Span を除く関数のシグネチャを併記した。
(以下のヴァリアントは SyntaxExtension::is_modern() で false を返す)
NormalTT { Box<TTMacroExpander>, Option<(NodeId, Span)>, bool, bool }- function 形式の procedural macro
TokenStream -> Box<MacResult>
MultiModifier(Box<MultiItemModifier>)- attribute 形式の procedural macro
(&MetaItem, Annotatable) -> Vec<Annotatable>
BuiltinDerive(BuiltinDeriveFn)- derive 形式の syntax extension
(&MetaItem, &Annotatable, &mut FnMut(Annotatable)) -> ()
(以下のヴァリアントは SyntaxExtension::is_modern() で true を返す)
ProcMacro(Box<ProcMacro>)- function 形式の procedural macro
TokenStream -> TokenStream
AttrProcMacro(Box<AttrProcMacro>)- attribute 形式の procedural macro
(TokenStream, TokenStream) -> TokenStream
ProcMacroDerive(Box<MultiItemModifier>, Vec<Symbol>)- derive 形式の procedural macro
(MetaItem, Annotable) -> Vec<Annotable>
(その他)
DeclMacro(Box<TTMacroExpander>, Option<(NodeId, Span)>)- declarative macro
- おそらく RFC 1584 で提案されている macros 2.0 のこと
TokenStream -> Box<MacResult>
- declarative macro
IdentTT(Box<IdentMacroExpander>, Span, bool)- おそらく
macro_rules!のことmacro_rules!自体の注入箇所が見当たらない…
(Ident, Vec<TokenTree>) -> Box<MacResult>
- おそらく
(謎)
MultiDecorator(Box<MultiItemDecorator>)- attribute 形式の syntax extension
#[derive(...)]がこれを用いているそう
(&MetaItem, &Annotatable, &mut FnMut(Annotatable)) -> ()BuiltinDeriveとの違いは…?- こちらは関数ポインタ以外も受け取れる
- attribute 形式の syntax extension
NormalTTはsyntax_extで組み込みマクロを登録するために用いられている。