How do I use a macro across module files?
我在同一板条箱中的单独文件中有两个模块,其中板条箱已启用macro_rules。我想在另一个模块中使用一个模块中定义的宏。
1 2 3 4 5 6 7 8
| // 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!()?
-
nope(不是afaik)-据报告,模块前缀被忽略(根据编译器消息)。
同一条板箱中的宏
1 2 3 4 5 6 7 8
| #[macro_use]
mod foo {
macro_rules! bar {
() => ()
}
}
bar!(); // works |
如果要在同一package箱中使用宏,则定义宏的模块需要属性#[macro_use]。
仅在定义宏后才能使用。这意味着这不起作用:
1 2 3 4 5 6 7 8
| bar!(); // ERROR: cannot find macro `bar!` in this scope
#[macro_use]
mod foo {
macro_rules! bar {
() => ()
}
} |
板条箱宏
要使用其他package箱中的macro_rules!宏,宏本身需要属性#[macro_export]。然后,导入箱可以通过use crate_name::macro_name;。
导入宏。
板条箱util
1 2 3 4
| #[macro_export]
macro_rules! foo {
() => ()
} |
板条箱user
1 2 3
| use util::foo;
foo!(); |
请注意,宏始终位于板条箱的顶层;因此,即使foo放在mod bar {}内,user板条箱仍必须写use util::foo;而不是use util::bar::foo;。
在Rust 2018之前,您必须通过将属性#[macro_use]添加到extern crate util;语句中来从其他package箱中导入宏。这将从util导入所有宏。或者,可以使用#[macro_use(cat, dog)]仅导入宏cat和dog。不再需要此语法。
有关宏的Rust编程语言一章,提供了更多信息。
-
"只有在定义宏后才能使用。" -这是关键,因为即使您正确完成了所有其他操作,您仍然可能遇到该错误。例如,如果您有模块macros和foo(使用macros中的宏),并且在lib.rs或main.rs中按字母顺序列出它们,则foo将在宏和代码不会编译。
-
^专家提示-这完全让我受益
-
另请注意,要在内部使用宏,#[macro_use]属性应位于每个模块和父模块等上。直到达到需要使用的位置为止。
-
这个答案对我不起作用。声明宏的模块具有#[macro_use],并且它首先在lib.rs中声明-仍然无法正常工作。 @Tens答案有所帮助,我在lib.rs的顶部添加了#[macro_use]-然后它起作用了。但是我仍然不确定最佳实践是什么,因为我在这里读到"您不从其他模块导入宏;而是从定义模块中导出宏"
-
我总是忘记了Rusts宏如何与模块一起使用。这是一个糟糕的系统,希望有一天会有更好的结果。
-
@十的暗示帮助了我。需要将#[macro_use]添加到完整路径上的所有mod语句中,以供父模块使用。那太疯狂了。太感谢了!
从Rust 1.1.0稳定版开始,此答案已过时。
您需要在macros.rs的顶部添加#![macro_escape],并使用《宏指南》中提到的使用mod macros;的方式将其包括在内。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| $ 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 |
供以后参考,
1 2
| $ rustc -v
rustc 0.13.0-dev (2790505c1 2014-11-03 14:17:26 +0000) |
- ID完全错过了该属性。谢谢!
-
顺便说一句,#[macro_export]属性在这里是不必要的。仅当宏应导出到外部板条箱用户时才需要。如果仅在板条箱内部使用宏,则不需要#[macro_export]。
-
非常感谢您的回答。我只想补充一下,如果您的something.rs文件使用其他模块,例如使用mod foobar;,并且此foobar模块使用macro.rs中的宏,则必须将mod macro;放在mod foobar;之前供程序编译。小事,但这不是一个明显的IMO。
-
(注:此答案现在已过时;我已经接受了卢卡斯提供的最新答案)
在包含宏的文件顶部添加#![macro_use]将导致所有宏都被拉入main.rs。
例如,假设此文件名为node.rs:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #![macro_use]
macro_rules! test {
() => { println!("Nuts"); }
}
macro_rules! best {
() => { println!("Run"); }
}
pub fn fun_times() {
println!("Is it really?");
} |
您的main.rs有时如下所示:
1 2 3 4 5 6 7 8
| mod node; //We're using node.rs
mod toad; //Also using toad.rs
fn main() {
test!();
best!();
toad::a_thing();
} |
最后,假设您有一个名为toad.rs的文件,该文件也需要以下宏:
1 2 3 4 5 6 7
| use node; //Notice this is 'use' not 'mod'
pub fn a_thing() {
test!();
node::fun_times();
} |
请注意,一旦使用mod将文件拖入main.rs中,其余文件便可以通过use关键字访问它们。
-
我添加了更多的说明。从rustc 1.22.1开始,这确实有效。
-
你确定吗? #在哪里记录?我找不到。它在这里不起作用。
-
当我发布它时,它起作用了,Rusts include system太糟糕了,它完全有可能不再起作用了。
-
@Markus请注意,#![macro_use]语句位于宏模块内部,而不位于外部。 #![...]语法对应于将属性应用于其包含的作用域,例如#],这将没有任何意义;它在语义上要求编译器在板条箱中的特定项目上启用某些功能,而不是在整个根板条箱中启用)。因此,就像@LukeDupin所说的那样,模块系统是一团糟,尽管其原因可能与乍看之下有所不同。
-
我确实希望这个答案提到结构不是完全习惯的(顺便说一句,我喜欢这个答案)。尽管具有(非)惯用性,但它的有趣之处在于,将其置于惯用形式的旁边使痛苦地显而易见,宏与模块系统的交互方式与通常的构造不同。或至少它散发出强烈的气味(如@Markus所抱怨的那样)。
-
之所以有效,是因为在main.rs中,行mod node;在mod toad;之前。使用rustfmt时请小心,因为它可能按字典顺序重新排列这些行。对我来说,解决方法是在这些行之间插入一个空的换行符。
-
"只有在定义宏后才能使用。"谢谢!
-
我更喜欢将注释放在mod声明上,因为它可以防止rustfmt更改顺序。
我在Rust 1.44.1中遇到了相同的问题,该解决方案适用于更高版本(已知适用于Rust 1.7)。
假设您有一个新项目,如下:
1 2 3 4
| src/
main.rs
memory.rs
chunk.rs |
在main.rs中,您需要注释要从源导入宏,否则,它对您不起作用。
1 2 3 4 5 6 7
| #[macro_use]
mod memory;
mod chunk;
fn main() {
println!("Hello, world!");
} |
因此在memory.rs中,您可以定义宏,并且不需要注释:
1 2 3 4 5 6 7
| macro_rules! grow_capacity {
( $x:expr ) => {
{
if $x < 8 { 8 } else { $x * 2 }
}
};
} |
最后,您可以在chunk.rs中使用它,并且您不需要在此处包含宏,因为它是在main.rs中完成的:
推荐的答案对我造成了困惑,以本文档为例,这也会有所帮助。
-
@Shepmaster赞成的答案在同一位置定义了宏和import语句,因此引起了混乱(对我而言)。我在定义中使用了#[macro_use]。编译器没有说它放错了位置。
-
谢谢您的回答!接受的答案也使我感到困惑,直到我读完你的解释才想出答案。
-
@Shepmaster在链接到的部分中没有提及宏的工作方式。您是要链接到本书的其他部分吗?
-
@detly否,因为我的评论所指出的内容比宏更广泛。这个回答者似乎对mod { ... }和mod some_file是同一件事并且都创建了一个模块感到困惑。接受的答案已经显示了#[macro_use]的用法,因此此答案实际上并没有提供任何新内容。
-
另外,请确保在main.rs中具有正确的mod顺序。如果您有mod chunk; mod memory;,则memory.rs中的宏调用将失败。