Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

% スコープ

マクロのスコープの決まり方は、やや直感に反することがあります。まず、言語内の他のすべてのものとは異なり、マクロはサブモジュール内でも可視のままです。

macro_rules! X { () => {}; }
mod a {
    X!(); // 定義済み
}
mod b {
    X!(); // 定義済み
}
mod c {
    X!(); // 定義済み
}
fn main() {}

: これらの例では、モジュールの内容が別々のファイルにある場合でも、すべてが同じ振る舞いをすることを覚えておいてください。

次に、これもまた言語内の他のすべてのものとは異なり、マクロはその定義のでのみアクセスできます。また、この例は、マクロがその定義スコープの外へ「漏れ出さない」ことを示している点にも注意してください。

mod a {
    // X!(); // 未定義
}
mod b {
    // X!(); // 未定義
    macro_rules! X { () => {}; }
    X!(); // 定義済み
}
mod c {
    // X!(); // 未定義
}
fn main() {}

明確にしておくと、この字句順序への依存性は、マクロを外側のスコープに移動した場合でも適用されます。

mod a {
    // X!(); // 未定義
}
macro_rules! X { () => {}; }
mod b {
    X!(); // 定義済み
}
mod c {
    X!(); // 定義済み
}
fn main() {}

しかし、この依存性はマクロ自体には適用されません。

mod a {
    // X!(); // 未定義
}
macro_rules! X { () => { Y!(); }; }
mod b {
    // X!(); // 定義済みだが、Y! は未定義
}
macro_rules! Y { () => {}; }
mod c {
    X!(); // 定義済みであり、Y! も同様
}
fn main() {}

マクロは #[macro_use] 属性を使用してモジュールからエクスポートできます。

mod a {
    // X!(); // 未定義
}
#[macro_use]
mod b {
    macro_rules! X { () => {}; }
    X!(); // 定義済み
}
mod c {
    X!(); // 定義済み
}
fn main() {}

マクロ内の識別子(他のマクロを含む)は展開時にのみ解決されるため、これがやや奇妙な形で相互作用することがある点に注意してください。

mod a {
    // X!(); // 未定義
}
#[macro_use]
mod b {
    macro_rules! X { () => { Y!(); }; }
    // X!(); // 定義済みだが、Y! は未定義
}
macro_rules! Y { () => {}; }
mod c {
    X!(); // 定義済みであり、Y! も同様
}
fn main() {}

もう一つの複雑な点は、extern crate に適用された #[macro_use] はこのようには振る舞わないことです。このような宣言は実質的にモジュールの先頭へ巻き上げられます。したがって、X!mac という外部クレートで定義されていると仮定すると、次のことが成り立ちます。

mod a {
    // X!(); // 定義済みだが、Y! は未定義
}
macro_rules! Y { () => {}; }
mod b {
    X!(); // 定義済みであり、Y! も同様
}
#[macro_use] extern crate macs;
mod c {
    X!(); // 定義済みであり、Y! も同様
}
# fn main() {}

最後に、これらのスコープの振る舞いは、#[macro_use](これは適用できません)を除いて、関数にも同様に適用される点に注意してください。

macro_rules! X {
    () => { Y!() };
}

fn a() {
    macro_rules! Y { () => {"Hi!"} }
    assert_eq!(X!(), "Hi!");
    {
        assert_eq!(X!(), "Hi!");
        macro_rules! Y { () => {"Bye!"} }
        assert_eq!(X!(), "Bye!");
    }
    assert_eq!(X!(), "Hi!");
}

fn b() {
    macro_rules! Y { () => {"One more"} }
    assert_eq!(X!(), "One more");
}

fn main() {
    a();
    b();
}

こうしたスコープ規則があるため、一般的な助言として、「クレート全体」でアクセス可能にすべきすべてのマクロは、ルートモジュールの最上部、他のどのモジュールよりも前に置くべきだとされています。これにより、それらが一貫して利用可能であることが保証されます。