关于rust:如何在模块文件中使用宏?

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'" ...这很有意义;宏系统先于模块系统运行。我该如何解决?


同一条板箱中的宏

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)]仅导入宏catdog。不再需要此语法。

有关宏的Rust编程语言一章,提供了更多信息。


从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)


在包含宏的文件顶部添加#![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关键字访问它们。


我在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中完成的:

1
grow_capacity!(8);

推荐的答案对我造成了困惑,以本文档为例,这也会有所帮助。