読者です 読者をやめる 読者になる 読者になる

Rustの組み込みマクロ

Rustのマクロの多くは macro_rules! で定義されるが、トークン列をトークン列に変換するものなら何でもマクロとして実装されうる。

Rustの標準ライブラリのマクロの多くは core::macros にて定義されている。

以下のマクロは通常の macro_rules! により定義される。

  • panic!
  • assert!
  • assert_eq!
  • assert_ne!
  • debug_assert!
  • debug_assert_eq!
  • debug_assert_ne!
  • try!
  • write!
  • writeln!
  • unreachable!
  • unimplemented!

以下のマクロは core::macros::builtin に宣言だけ存在するが、実装はコンパイラ組み込みである。

  • format_args!
  • env!
  • option_env!
  • concat_idents!
  • concat!
  • line!
  • column!
  • file!
  • stringify!
  • include_str!
  • include_bytes!
  • module_path!
  • cfg!
  • include!

これらはコンパイラ組み込みであり、 syntax_ext::register_builtins で登録されている。

これはマクロに限らない一般の構文拡張を管理するデータベース syntax::ext::base で管理されている。このデータベースには以下のようなエントリがある(抜粋):

  • MultiModifier: Pythonの属性のように、itemを受け取り別のitemに変換する構文拡張。
  • ProcMacro: トークン列をトークン列に変換する、構文拡張の一般的なインターフェース。
  • AttrProcMacro: ProcMacro に似ているが、属性込みで変換できるインターフェース。
  • NormalTT: ProcMacro に似ているが、よりマクロ呼び出しに特化したインターフェースになっている。トークン列ではなく、構文要素を返す。
  • IdentTT: 識別子を識別子に変換する。

マクロ構文では ProcMacro, NormalTT, IdentTT のみ取り扱われる。他の構文拡張はattrやderiveで使われる。

macro_rules! で定義されるマクロは syntax::ext::tt::macro_rulesNormalTT に変換される。また組み込みマクロも NormalTT である。なお、 macro_rules! 自身はかなり特殊な扱いで、構文解析器が場合分けを行う。

組み込みマクロは、上で説明したように、トークン列を構文要素に変換する関数がコンパイラ内に実装されているという以上のことはない。 include_str!stringify! のようなCプリプロセッサのような処理は syntax::ext::source_util で実装されている。それ以外の組み込みマクロの実装は src/libsyntax_ext内の各ソースファイルが提供している。

まとめ

Rustにおけるマクロは、トークン列を構文要素に変換するメタ関数に他ならない。多くは macro_rules! により構文解析時に定義されるが、これで定義できるのはごく簡単なマクロだけである。より複雑だったり、処理系組み込みの機能を提供するマクロは、コンパイラに組み込みであったり、 別のcrateでプロシージャマクロとして定義されていたりする。