1.32.0
(2018 版)的替代方法
请注意,虽然来自 @lukas-kalbertodt的说明仍然是最新的并且运行良好,但必须记住宏的特殊命名空间规则的想法可能会让某些人感到烦恼。
在 2018 版及以后的版本1.32.0
中,自 Rust 版本以来,还有另一种方法也有效,恕我直言,它的好处是让教学更容易(例如,它#[macro_use]
已经过时了)。关键思想如下:
重新导出的宏的行为与任何其他项目(函数、类型、常量等)一样:它在重新导出发生的模块内命名空间。
例子
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;
}
我们还可以这样做:
fn main() {
let _: A;
}
struct Foo {}
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 宏
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]
-ed 宏
应用于#[macro_export]
宏定义使其在定义它的模块之后可见(以便与非#[macro_export]
ed 宏的行为一致),但它也将宏置于 crate 的根(定义宏的位置) ),以绝对路径方式。
这意味着pub use macro_name;
在宏定义之后的右边,或者pub use crate::macro_name;
在那个 crate 的任何模块中的右边都可以工作。
- 注意:为了使重新导出不与“在箱子根部导出”机制发生冲突,不能在箱子本身的根部进行。
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
此外,一些误报 lints 可能会触发: