衛生性(Hygiene)
衛生性(Hygiene)はマクロに関する重要な概念です。 衛生性とは、マクロが周辺のコンテキストに影響を与えたり逆に影響受けたりすることなく、自身の構文コンテキストの中で動作する能力を指します。 言いかえれば、任意の構文拡張の呼び出しは周辺のコンテキストに干渉するべきではないということを意味します。
理想的には、Rustのすべての構文拡張は完全に衛生的であってほしいところですが、残念ながらそうではないので、十分衛生的ではない構文拡張を書くことがないよう注意を払うべきです。 ここから一般的な衛生性の概念の説明に入ります。なお、これについてはRustが提供する別の種類の構文拡張の衛生性に関する章でも言及します。
衛生性は主に構文拡張が出力する識別子とパス(path)に影響します。 簡単にいうと、構文拡張が生成した識別子を、構文拡張を呼び出した環境から参照できないとき、構文拡張はその識別子に関して衛生的であるといえます。 同様に、構文拡張の内部で利用されている識別子が、構文拡張の外部で定義されたものを参照できないときも、構文拡張は衛生的であるといえます。
Note: 「生成」「利用」という用語は、識別子がどの位置に出現しているかを表すものです。
struct Foo {}
におけるFoo
やlet foo = …;
におけるfoo
は、その名前のもとで新しい何かを導入したという意味で「生成」されたといえます。 一方でfn foo(_: Foo) {}
におけるFoo
やfoo + 3
におけるfoo
は、既存の何かを参照するという意味で「利用」だといえます。
例を挙げて説明するのが一番でしょう。
make_local
という構文拡張があって、let local = 0;
に展開されるとしましょう。これは local
という識別子を生成します。
次のようなコード片を考えます:
make_local!();
assert_eq!(local, 0);
もし assert_eq!(local, 0);
における local
が、この構文拡張が定義したローカル変数に解決されるならば、この構文拡張は(少なくともローカルな名前・束縛(bindings)に関していえば)衛生的ではないということになります。
今度は use_local
という構文拡張があって、local = 42;
に展開されるとしましょう。これは local
という識別子を利用します。
次のようなコード片を考えます:
let mut local = 0;
use_local!();
もし呼び出された構文拡張の内部の local
が、その呼び出しの前に定義されたローカル変数に解決されるのであれば、この構文拡張はやはり衛生的ではないということになります。
以上は衛生性の一般概念への比較的短い導入です。
衛生性については、 macro_rules!
の衛生性と手続き的マクロの衛生性の章で、各々に固有の性質に言及しながら、より深いところまで説明します。