截至1.32.0
(2018版)的备选方案
请注意,虽然@lukas-kalbertodt提供的指南仍然是最新的且有效,但是对于一些人来说,必须记住宏的特殊命名空间规则可能很烦人。
从2018版开始,在Rust的版本1.32.0
及以上,有另一种方法同样有效,并且在我看来有一个好处,即更容易教授(例如,它使#[macro_use]
过时)。关键思想如下:
重新导出的宏与任何其他项(函数、类型、常量等)行为相同:它在重新导出发生的模块中命名空间。
然后可以使用完全限定路径引用它。
也可以本地使用/引入范围,以无资格方式引用它。
示例
macro_rules! macro_name { ... }
pub(crate) use macro_name;
\{\{就是这样了。相当简单,是吧?\}\}
请随意继续阅读,只有当你不怕信息过载时 ;) 我会详细解释为什么、如何以及何时会起作用。
更详细的解释
为了重新导出 (pub(...) use ...
) 一个宏,我们需要引用它!这就是原回答中提到的规则有用的地方:一个宏总是可以在定义它的模块内部命名,但只有在那个定义之后才能这样做。
macro_rules! my_macro { ... }
my_macro!(...);
my_macro!(...);
macro_rules! my_macro { ... }
基于这一点,我们可以在定义后重新导出一个宏;因此,重新导出的名称本身是与位置无关的,在Rust中所有其他全局项也是如此。
In the same fashion that we can do:
struct Foo {}
fn main() {
let _: Foo;
}
We can also do:
fn main() {
let _: A;
}
struct Foo {}
use Foo as A;
The same applies to other items, such as functions, but also to macros!
fn main() {
a!();
}
macro_rules! foo { ... }
use foo as a;
And it turns out that we can write use foo as foo;
, or the common use foo;
shorthand, and it still works.
唯一剩下的问题是:
pub(crate)
还是pub
?
详细示例
For a non-#[macro_export]
ed macro
mod foo {
use super::example::my_macro;
my_macro!(...);
}
mod example {
macro_rules! my_macro { ... }
pub(crate) use my_macro;
}
example::my_macro!(...);
For a #[macro_export]
-ed macro
Applying #[macro_export]
on a macro definition makes it visible after the very module where it is defined (so as to be consistent with the behavior of non-#[macro_export]
ed macros), but it also puts the macro at the root of the crate (where the macro is defined), in an absolute path fashion.
This means that a pub use macro_name;
right after the macro definition, or a pub use crate::macro_name;
in any module of that crate will work.
- Note: in order for the re-export not to collide with the "exported at the root of the crate" mechanic, it cannot be done at the root of the crate itself.
pub mod example {
#[macro_export]
macro_rules! my_macro { ... }
pub use my_macro;
}
pub mod foo {
pub use crate::my_macro;
}
当使用
pub / pub(crate) use macro_name;
时,请注意由于Rust中命名空间的工作方式,您可能还会重新导出常量/函数或类型/模块。这也会导致全局可用的宏(例如
#[test]
,
#[allow(...)]
,
#[warn(...)]
等)存在问题。
为了解决这些问题,请记住在重新导出项目时可以重命名它:
macro_rules! __test__ { ... }
pub(crate) use __test__ as test;
macro_rules! __warn__ { ... }
pub(crate) use __warn__ as warn;
此外,一些误报的lint可能会触发:
module::my_macro!()
吗? - u_mulder