如何在模块文件中使用宏?


105

我在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)-据报告,模块前缀被忽略(根据编译器消息)。
用户

Answers:


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!();

请注意,宏始终位于板条箱的顶层;所以即使在箱子foomod bar {}user箱子还是必须书写use util::foo;不是 use util::bar::foo;

在Rust 2018之前,您必须通过将属性添加#[macro_use]extern crate util;语句来从其他包装箱中导入宏。那会从中导入所有宏util。或者,#[macro_use(cat, dog)]可用于仅导入宏catdog。不再需要此语法。

有关的Rust编程语言一章提供了更多信息。


32
“只有在定义宏后才能使用。” -这很关键,因为即使您正确完成了所有其他操作,也可能遇到该错误。例如,如果您有模块macrosfoo(使用中的宏macros),并在lib.rs或main.rs中按字母顺序列出它们,则foo将在宏之前加载,并且代码不会编译。
neverfox

9
^专家提示-这完全让我
semore_1267 '18 -10-18

6
还要注意,在内部使用宏时,该#[macro_use]属性应位于每个模块和父模块等上,直到到达需要使用它的位置为止。

1
这个答案对我不起作用。声明宏的模块已经存在,#[macro_use]并且它首先在lib.rs中声明-仍然不起作用。#[macro_use]@Ten的回答很有帮助,我将其添加到lib.rs的顶部-然后它起作用了。但是我仍然不确定最佳实践是什么,因为我在这里读到“您不从其他模块导入宏;而是从定义的模块导出宏”
Sorin Bolos

我总是忘记Rust的宏如何与模块一起使用。这是一个糟糕的系统,希望有一天会有更好的系统。
哈奇·摩尔

21

从Rust 1.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]则不需要。
弗拉基米尔·马特维耶夫(Fladimir Matveev)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]语句位于宏模块内部,而不位于外部。该#![...]语法对应于具有属性适用于他们的包含范围,例如,#![feature(...)](很明显,如果写,因为这是没有意义的#[feature(...)],这将语义要求编译器能够在一个箱子的具体项目,而不是整根板条箱的某些功能)。因此,就像@LukeDupin所说的那样,模块系统是一团糟,尽管其原因可能与乍一看不同。
用户

我确实希望这个答案能说明结构不是完全惯用的(此外,我喜欢这个答案)。尽管具有(非)惯用性,但仍很有趣,因为将其置于惯用形式旁边会使痛苦地显而易见,宏与模块系统的交互方式与通常的构造不同。或至少它散发出强烈的气味(如@Markus所抱怨的那样)。
用户

3

我在Rust 1.44.1中遇到了相同的问题,该解决方案适用于更高版本(已知适用于Rust 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答案引起了混乱对我来说,与这个文档的例子,这将有助于太。


从字面上看,可接受的答案就是第一个代码块的第一行:#[macro_use] mod foo {
Shepmaster

2
@Shepmaster赞成的答案在同一位置定义了宏和import语句,因此引起了混乱(对我而言)。我#[macro_use]在定义中使用。编译器没有说它放错了位置。
knh190 '20

然后,您可能希望重新阅读doc.rust-lang.org/book/…
Shepmaster

感谢您的回答!接受的答案也使我感到困惑,直到我读完你的解释才弄清楚。
Prgrm.celeritas

1
此外,一定要拥有正确的顺序modmain.rs。如果有mod chunk; mod memory;,则宏调用memory.rs将失败。
ineiti
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.