Exporting declarative macro in shared module in `benches`

Hello all, I have a crate that looks something like:

├── Cargo.toml
├── src
│   └── lib.rs
├── benches
     └── common.rs
     └── my_bench.rs

Inside common.rs, I have a macro intended for use in my other benchmark files, e.g. my_bench.rs:

// common.rs

#[macro_export]
macro_rules! my_macro {
    ($key:expr) => { 
        // does something
    }
}

// some other functions
pub fn helper_func() {}

Here, I used #[macro_export] to export my_macro. From my understanding, this means that my_macro will be declared at the crate root scope.

But when I try to do this in my_bench.rs:

mod common;
use crate::my_macro;

// benchmark code
// ...

I get the error:

error[E0255]: the name `my_macro` is defined multiple times
 --> my-crate/benches/common.rs:4:1
  |
4 | macro_rules! my_macro {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `my_macro` redefined here
  |
 ::: my-crate/benches/my_bench.rs:7:5
  |
7 | use crate::my_macro;
  |     ------------------------ previous import of the macro `my_macro` here
  |
  = note: `my_macro` must be defined only once in the macro namespace of this module

The solution to get around this, it turns out, is simply to omit use crate::my_macro, and everything works fine! But why is this the case? Does this have something to do with how benchmarks are compiled? In case it matters, my_bench has harness = false as well in Cargo.toml.

Follow-up

Another thing I tried, which works, is to replace #[macro_export] in common.rs with pub(crate) use my_macro (got the idea from here):

// common.rs

macro_rules! my_macro {
	// ...
}

pub(crate) use my_macro;

Then in my_bench.rs:

mod common;
use common::{helper_func, my_macro};

However, now I get the warning:

warning: unused macro definition: `my_macro`
 --> my-crate/benches/common.rs:3:14
  |
3 | macro_rules! my_macro {
  |              ^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_macros)]` on by default

warning: unused import: `my_macro`
  --> my-crate/benches/common.rs:27:16
   |
27 | pub(crate) use my_macro;
   |                ^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(unused_imports)]` on by default

Once again, I don't quite get why is this the case. Any pointers, explanations, or links to detailed explanations would be greatly appreciated!

It indeed incurs two macros with same name which is exactly reported from the error

  • #[macro_export] defines the macro in the root
  • use crate::my_macro; brings the macro agian into the root: see the fine result if brings it via an alias Rust Playground

Update: to have the path-based scope for macros, you can use this trick ( play )

use crate::common::m;

mod common {
    #[macro_export]
    macro_rules! my_macro {
        () => { };
    }
    pub use my_macro as m; // alias + reexport for path-based scope
}

fn main() {
    my_macro!();
    m!(); // works
    crate::common::m!(); // works
}
1 Like