モジュールファイル間でマクロを使用するにはどうすればよいですか?


104

同じクレート内の別々のファイルに2つのモジュールがあり、クレートがmacro_rules有効になっています。あるモジュールで定義されたマクロを別のモジュールで使用したい。

// macros.rs
#[macro_export] // or not? is ineffectual for this, afaik
macro_rules! my_macro(...)

// something.rs
use macros;
// use macros::my_macro; <-- unresolved import (for obvious reasons)
my_macro!() // <-- how?

私は現在、コンパイラエラー " macro undefined: 'my_macro'" ...をヒットしました。これは理にかなっています。マクロシステムは、モジュールシステムの前に実行されます。どうすればそれを回避できますか?


使用しないでくださいmodule::my_macro!()?
u_mulder 2014年

2
nope(afaikではない)-モジュールプレフィックスは無視されると報告されています(コンパイラメッセージによると)。
ユーザー

回答:


144

同じクレート内のマクロ

#[macro_use]
mod foo {
    macro_rules! bar {
        () => ()
    }
}

bar!();    // works

同じクレートでマクロを使用する場合、マクロが定義されているモジュールには属性が必要です#[macro_use]

マクロは、定義された後でのみ使用できます。これは、これが機能しないことを意味します。

bar!();  // ERROR: cannot find macro `bar!` in this scope

#[macro_use]
mod foo {
    macro_rules! bar {
        () => ()
    }
}

木枠全体のマクロ

macro_rules!他のクレートからマクロを使用するには、マクロ自体に属性が必要です#[macro_export]。インポートするクレートは、を介してマクロをインポートできuse crate_name::macro_name;ます。

クレート util

#[macro_export]
macro_rules! foo {
    () => ()
}

クレート user

use util::foo;

foo!();

マクロは常にクレートのトップレベルに存在することに注意してください。したがって、のfoo中にあるmod bar {}としても、userクレートは書く必要があり、書く必要はuse util::foo;ありません use util::bar::foo;

Rust 2018より前#[macro_use]は、extern crate util;ステートメントに属性を追加して、他のクレートからマクロをインポートする必要がありました。これにより、からすべてのマクロがインポートされutilます。または、#[macro_use(cat, dog)]マクロcatとをインポートするためにのみ使用できますdog。この構文はもう必要ないはずです。

詳細については、マクロに関するRustプログラミング言語の章を参照してください


32
「マクロは、定義された後でのみ使用できます。」-他のすべてのことを正しく行った場合でも、そのエラーが発生する可能性があるため、これは重要です。たとえば、モジュールmacrosfoo(からのマクロを使用するmacros)があり、それらをlib.rsまたはmain.rsにアルファベット順にリストすると、マクロの前にfooがロードされ、コードはコンパイルされません。
neverfox 2017

9
^プロのヒント-これは完全に私を手に入れました
semore_1267 2018年

6
また、内部でマクロを使用する場合、#[macro_use]属性は、使用する必要があるポイントに到達するまで、すべてのモジュールや親モジュールなどに存在する必要があることに注意してください。
テン

1
この答えは私にはうまくいきませんでした。マクロを宣言したモジュールにはが#[macro_use]あり、lib.rsで最初に宣言されましたが、それでも機能しませんでした。#[macro_use]@Tenの答えが役に立ち、lib.rsの先頭に追加しました-それでうまくいきました。しかし、「他のモジュールからマクロをインポートするのではなく、定義するモジュールからマクロをエクスポートする」とここで読んだので、ベストプラクティスが何であるかはまだ
わかりません

Rustのマクロがモジュールでどのように機能するかを常に忘れています。それはひどいシステムであり、いつかもっと良いシステムがあることを願っています。
ハッチムーア

21

この回答は、Rust1.1.0の時点では古くなっています-安定しています。


マクロガイドに記載されているように、#![macro_escape]上部に追加し、macros.rsを使用しmod macros;て含める必要があります

$ cat macros.rs
#![macro_escape]

#[macro_export]
macro_rules! my_macro {
    () => { println!("hi"); }
}

$ cat something.rs
#![feature(macro_rules)]
mod macros;

fn main() {
    my_macro!();
}

$ rustc something.rs
$ ./something
hi

将来の参考のために、

$ rustc -v
rustc 0.13.0-dev (2790505c1 2014-11-03 14:17:26 +0000)

私はその属性を完全に見逃していました。ありがとう!
ユーザー

4
ところで、#[macro_export]ここでは属性は不要です。マクロを外部のクレートユーザーにエクスポートする必要がある場合にのみ必要です。マクロがクレート内でのみ使用される場合は、#[macro_export]必要ありません。
ウラジミールマトベーエフ2014年

1
答えてくれてありがとう。追加したいのは、something.rsファイルが他のモジュールを使用している場合、たとえば、mod foobar;で、このfoobarモジュールがからのマクロを使用しているmacro.rs場合、プログラムをコンパイルするmod macro; に置く必要があることmod foobar;です。些細なことですが、これは明らかなIMOではありません。
conradkleinespel 2015

2
(nbこの回答は現在古くなっています。Lukasからの最新の回答を受け入れました)
ユーザー

9

#![macro_use]マクロを含むファイルの先頭に追加すると、すべてのマクロがmain.rsにプルされます。

たとえば、このファイルの名前がnode.rsであると仮定します。

#![macro_use]

macro_rules! test {
    () => { println!("Nuts"); }
}

macro_rules! best {
    () => { println!("Run"); }
}

pub fn fun_times() {
    println!("Is it really?");
}

main.rsは、次のようになります。

mod node;  //We're using node.rs
mod toad;  //Also using toad.rs

fn main() {
    test!();
    best!();
    toad::a_thing();
}

最後に、次のマクロも必要とするtoad.rsというファイルがあるとします。

use node; //Notice this is 'use' not 'mod'

pub fn a_thing() {
  test!();

  node::fun_times();
}

ファイルがでmain.rsにプルされるとmod、残りのファイルはuseキーワードを介してそれらにアクセスできることに注意してください。


さらに説明を追加しました。rustc 1.22.1の時点で、これは機能します。
ルークデュパン

本気ですか?この#![macro_use](#[macro_use]ではない)はどこに文書化されていますか?見つかりません。ここでは機能しません。
マルクス

これは私が投稿したときに機能しました。Rustのインクルードシステムは非常にひどい混乱であり、これが機能しなくなる可能性があります。
ルークデュパン

@Markus注意#![macro_use]文があるINSIDEない外、マクロモジュール。#![...]持つ属性へのシンタックス対応は、それを含むスコープ、例えばに適用されます#![feature(...)](のように書かれている場合、明らかにこれは意味がありません#[feature(...)]。それは意味的にコンパイラはクレート内の特定のアイテム、全体ではなく、ルートクレート上の特定の機能を有効にすることが必要となります)。したがって、@ LukeDupinが言ったように、モジュールシステムは混乱していますが、一見した理由とは異なる理由かもしれません。
ユーザー

この答えが、構造が正確に慣用的ではないことを述べていることを願っています(それはさておき、私は答えが好きです)。その(非)慣用性にもかかわらず、それを慣用的な形式の隣に配置すると、マクロが通常の構造とは異なる方法でモジュールシステムと相互作用することが痛々しいほど明白になるため、興味深いです。または、少なくとも強い匂いを放ちます(@Markusがそれに不満を持っていることによって示されているように)。
ユーザー

3

私がしている同じ問題に出くわした錆1.44.1に、この溶液は(錆1.7のために働いて知られている)それ以降のバージョンで動作します。

次のような新しいプロジェクトがあるとします。

src/
    main.rs
    memory.rs
    chunk.rs

ではmain.rs、あなたはそうでない場合、それはあなたのためにしないだろう、あなたはソースからのマクロをインポートしていることを注釈する必要があります。

#[macro_use]
mod memory;
mod chunk;

fn main() {
    println!("Hello, world!");
}

したがって、memory.rsではマクロを定義でき、注釈は必要ありません。

macro_rules! grow_capacity {
    ( $x:expr ) => {
        {
            if $x < 8 { 8 } else { $x * 2 }
        }
    };
}

最後に、chunk.rsで使用できます。また、main.rsで実行されるため、ここにマクロを含める必要はありません。

grow_capacity!(8);

upvoted答えはで、私のために混乱を引き起こした例により、このdoc、それはあまりにも役立つだろう。


受け入れられた答えは、文字通り、最初のコードブロックの最初の行としてそれを持っています:#[macro_use] mod foo {
シェップマスター

2
@Shepmasterは、賛成の回答にマクロの定義とインポートステートメントが同じ場所にあるため、混乱を引き起こしました(私にとって)。私は#[macro_use]定義で使用していました。コンパイラは、それが置き忘れられているとは言いません。
knh1 9020

doc.rust-lang.org/book/…を読み直したいと思うかもしれません。
シェップマスター

この答えをありがとう!私も受け入れられた答えに混乱し、あなたの説明を読むまでそれを理解することができませんでした。
Prgrm.celeritas

1
また、正しい順序持つようにしてくださいmodではmain.rs。がある場合mod chunk; mod memory;、マクロ呼び出しmemory.rsは失敗します。
ineiti
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.