use*_*ser 59 module rust rust-macros
我在同一个包中的两个模块中有两个模块,其中包已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?
Run Code Online (Sandbox Code Playgroud)
我目前遇到编译器错误" macro undefined: 'my_macro'"...这是有道理的; 宏系统在模块系统之前运行.我该如何解决这个问题?
Luk*_*odt 90
#[macro_use]
mod foo {
macro_rules! bar {
() => ()
}
}
bar!(); // works
Run Code Online (Sandbox Code Playgroud)
如果要在同一个包中使用宏,则定义宏的模块需要该属性#[macro_use].
宏只能在定义后使用.这意味着这不起作用:
bar!(); // ERROR: cannot find macro `bar!` in this scope
#[macro_use]
mod foo {
macro_rules! bar {
() => ()
}
}
Run Code Online (Sandbox Code Playgroud)
要使用macro_rules!其他包中的宏,宏本身需要该属性#[macro_export].然后导入包可以通过导入宏use crate_name::macro_name;.
// --- Crate `util` ---
#[macro_export]
macro_rules! foo {
() => ()
}
// --- Crate `user` ---
use util::foo;
foo!();
Run Code Online (Sandbox Code Playgroud)
注意:宏总是位于箱子的顶层; 因此,即使foo是在里面mod bar {},user箱子仍然必须写,use util::foo;而不是 use util::bar::foo;.
(在Rust 2018之前,你必须通过#[macro_use]向该extern crate util;语句添加属性来从其他包中导入宏.这将导入所有宏util.或者,#[macro_use(cat, dog)]可以用于仅导入宏cat和dog.此语法不再是必需的.)
有关更多信息,请参阅The Rust Programming Language.
Dog*_*ert 15
从Rust 1.1.0-stable开始,这个答案已经过时了.
您需要#![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
Run Code Online (Sandbox Code Playgroud)
备查,
$ rustc -v
rustc 0.13.0-dev (2790505c1 2014-11-03 14:17:26 +0000)
Run Code Online (Sandbox Code Playgroud)
Luk*_*pin 13
添加#![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?");
}
Run Code Online (Sandbox Code Playgroud)
您的 main.rs 有时会如下所示:
mod node; //We're using node.rs
mod toad; //Also using toad.rs
fn main() {
test!();
best!();
toad::a_thing();
}
Run Code Online (Sandbox Code Playgroud)
最后,假设您有一个名为 toad.rs 的文件,它也需要这些宏:
use node; //Notice this is 'use' not 'mod'
pub fn a_thing() {
test!();
node::fun_times();
}
Run Code Online (Sandbox Code Playgroud)
请注意,一旦使用 将文件拉入 main.rs mod,其余文件就可以通过use关键字访问它们。
Dan*_*H-M 13
1.32.0(2018 版)请注意,虽然来自 @lukas-kalbertodt的说明仍然是最新的并且运行良好,但对于某些人来说,必须记住宏的特殊命名空间规则的想法可能很烦人。
事实证明,在 2018 版及以后的版本1.32.0中,自Rust版本以来,还有另一种方法也有效,并且具有好处,恕我直言,使其更易于教学(例如,它#[macro_use]已过时)。关键思想如下:
重新导出的宏的行为与任何其他项(函数、类型、常量等)一样:它在重新导出发生的模块内具有命名空间。
然后可以使用完全限定的路径引用它。
它也可以在本地used / 带入范围,以便以不合格的方式引用它。
macro_rules! macro_name { ... }
pub(crate) use macro_name; // Now classic paths Just Work™
Run Code Online (Sandbox Code Playgroud)
就是这样。很简单吧?
请随意继续阅读,但前提是您不害怕信息过载;) 我将尝试详细说明为什么、如何以及何时执行此操作。
为了重新导出 ( pub(...) use ...) 一个宏,我们需要参考它!这就是原始答案中的规则有用的地方:宏始终可以在宏定义发生的模块中命名,但只能在该定义之后命名。
macro_rules! my_macro { ... }
my_macro!(...); // OK
Run Code Online (Sandbox Code Playgroud)
// Not OK
my_macro!(...); /* Error, no `my_macro` in scope! */
macro_rules! my_macro { ... }
Run Code Online (Sandbox Code Playgroud)
在此基础上,我们可以在定义后重新导出一个宏;然后,重新导出的名称本身与位置无关,就像 Rust 中的所有其他全局项一样
以我们可以做的相同方式:
struct Foo {}
fn main() {
let _: Foo;
}
Run Code Online (Sandbox Code Playgroud)
我们还可以这样做:
struct Foo {}
fn main() {
let _: A;
}
use Foo as A;
Run Code Online (Sandbox Code Playgroud)
这同样适用于其他项目,例如函数,也适用于宏!
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!
Run Code Online (Sandbox Code Playgroud)
事实证明,我们可以写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
Run Code Online (Sandbox Code Playgroud)
#[macro_export]-ed 宏应用#[macro_export]宏定义使其在定义它的模块之后可见(以便与未#[macro_export]编辑的宏的行为一致),但它也将宏放在 crate 的根部(宏定义的位置) ),以绝对路径方式。
这意味着pub use macro_name;宏定义之后的 a 或pub use crate::macro_name;该 crate 的任何模块中的a都可以工作。
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`
}
Run Code Online (Sandbox Code Playgroud)
使用 时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
Run Code Online (Sandbox Code Playgroud)
此外,一些误报 lint 可能会触发:
从触发快乐的clippy工具中,当这个技巧以任何方式完成时;
从rustc它本身,当这是在macro_rules!函数体内发生的定义上完成时:https : //github.com/rust-lang/rust/issues/78894
我在 Rust 1.44.1 中遇到了同样的问题,这个解决方案适用于更高版本(已知适用于 Rust 1.7)。
假设您有一个新项目:
src/
main.rs
memory.rs
chunk.rs
Run Code Online (Sandbox Code Playgroud)
在main.rs 中,您需要注释您是从源导入宏,否则,它将不适合您。
src/
main.rs
memory.rs
chunk.rs
Run Code Online (Sandbox Code Playgroud)
因此,在memory.rs 中,您可以定义宏,并且不需要注释:
#[macro_use]
mod memory;
mod chunk;
fn main() {
println!("Hello, world!");
}
Run Code Online (Sandbox Code Playgroud)
最后你可以在chunk.rs 中使用它,你不需要在这里包含宏,因为它是在 main.rs 中完成的:
macro_rules! grow_capacity {
( $x:expr ) => {
{
if $x < 8 { 8 } else { $x * 2 }
}
};
}
Run Code Online (Sandbox Code Playgroud)
该upvoted答案引起了混乱对我来说,与这个文档的例子,这将有助于太。
| 归档时间: |
|
| 查看次数: |
24180 次 |
| 最近记录: |