我在同一个包中的两个单独文件中有两个模块,其中包已启用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?
我目前遇到编译器错误&#34; macro undefined: 'my_macro'
&#34; ...这是有道理的;宏系统在模块系统之前运行。我该如何解决这个问题?
答案 0 :(得分:64)
#[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;
导入宏。
// --- Crate `util` ---
#[macro_export]
macro_rules! foo {
() => ()
}
// --- Crate `user` ---
use util::foo;
foo!();
注意:宏总是位于包的顶层;因此即使foo
位于mod bar {}
内,user
包仍需要写use util::foo;
而不 use util::bar::foo;
。
(在Rust 2018之前,您必须通过将属性#[macro_use]
添加到extern crate util;
语句来从其他包中导入宏。这将从util
导入所有宏。或者,{{ 1}}可用于仅导入宏#[macro_use(cat, dog)]
和cat
。此语法不再是必需的。)
The Rust Programming Language中提供了更多信息。
答案 1 :(得分:15)
从Rust 1.1.0-stable开始,这个答案已经过时了。
您需要在#![macro_escape]
的顶部添加macros.rs
,并使用Macros Guide中提到的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)
供将来参考,
{{1}}
答案 2 :(得分:7)
1.32.0
(2018 年版)请注意,虽然 the instructions from @lukas-kalbertodt 仍然是最新的并且运行良好,但对于某些人来说,必须记住宏的特殊命名空间规则的想法可能很烦人。
事实证明,在 2018 年及以后的版本中,从 Rust 的 1.32.0
版本开始,还有另一种方法也行得通,而且它的好处是,恕我直言,它使教学更容易(< em>eg,它使 #[macro_use]
过时)。核心思想如下:
然后可以使用完全限定的路径引用它。
它也可以在本地 use
d / 带入作用域,以便以非限定方式引用它。
macro_rules! macro_name { ... }
pub(crate) use macro_name; // Now classic paths Just Work™
就是这样。很简单吧?
请继续阅读,但前提是您不害怕信息过载;) 我将尝试详细说明为什么、如何以及何时执行此操作。
为了重新导出(pub(...) use ...
)一个宏,我们需要引用它!这就是原始答案中的规则有用的地方:宏始终可以在宏定义所在的模块中命名,但只能在之后该定义。
macro_rules! my_macro { ... }
my_macro!(...); // OK
// Not OK
my_macro!(...); /* Error, no `my_macro` in scope! */
macro_rules! my_macro { ... }
基于此,我们可以在定义之后重新导出一个宏;然后,重新导出的名称本身与位置无关,就像 Rust 中的所有其他全局项 ?
以我们可以做的相同方式:
struct Foo {}
fn main() {
let _: Foo;
}
我们也可以:
struct Foo {}
fn main() {
let _: A;
}
use Foo as A;
这同样适用于其他项目,例如函数,也适用于宏!
fn main() {
a!();
}
macro_rules! foo { ... } // foo is only nameable *from now on*
use foo as a; // but `a` is now visible all around the module scope!
事实证明,我们可以编写 use foo as foo;
,或者常见的 use foo;
简写,它仍然有效。
剩下的唯一问题是:pub(crate)
还是 pub
?
对于 #[macro_export]
-ed 宏,您可以使用任何您想要的隐私;通常pub
。
对于其他 macro_rules!
宏,您不能超过 pub(crate)
。
#[macro_export]
编辑的宏mod foo {
use super::example::my_macro;
my_macro!(...); // OK
}
mod example {
macro_rules! my_macro { ... }
pub(crate) use my_macro;
}
example::my_macro!(...); // OK
#[macro_export]
的宏在宏定义上应用 #[macro_export]
使其在定义它的模块之后可见(以便与非 #[macro_export]
ed 宏的行为保持一致),但它也以绝对路径方式将宏放在 crate 的根目录下(定义宏的地方)。
这意味着紧跟在宏定义之后的 pub use macro_name;
或该 crate 的任何模块中的 pub use crate::macro_name;
都可以工作。
pub mod example {
#[macro_export] // macro nameable at `crate::my_macro`
macro_rules! my_macro { ... }
pub use my_macro; // macro nameable at `crate::example::my_macro`
}
pub mod foo {
pub use crate::my_macro; // macro nameable at `crate::foo::my_macro`
}
使用 pub / pub(crate) use macro_name;
时,请注意,鉴于命名空间在 Rust 中的工作方式,您还可能会重新导出常量/函数或类型/模块。这也会导致全局可用的宏出现问题,例如 #[test]
、#[allow(...)]
、#[warn(...)]
、等
为了解决这些问题,请记住您可以在重新导出时重命名项目:
macro_rules! __test__ { ... }
pub(crate) use __test__ as test; // OK
macro_rules! __warn__ { ... }
pub(crate) use __warn__ as warn; // OK
此外,一些误报 lint 可能会触发:
来自触发快乐 clippy
工具,当这个技巧以任何方式完成时;
来自 rustc
本身,当在函数体内发生的 macro_rules!
定义上完成此操作时:https://github.com/rust-lang/rust/issues/78894
答案 3 :(得分:3)
我在Rust 1.44.1中拥有came across the same problem,并且该解决方案适用于更高版本(已知适用于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 answer引起了我的困惑,this doc by example也很有帮助。
答案 4 :(得分:1)
将#![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?");
}
你的主人会看起来像下面这样:
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();
}
请注意,一旦文件被mod
拉入main.rs,其余文件就可以通过use
关键字访问它们。