4

Crates can re-export crates on which they depend. In this example, the stm32f103xx-hal crate does that: pub extern crate stm32f103xx;.

In my code, I depend on stm32f103xx-hal. Now I want to use the interrupt!() macro that is exported by the stm32f103xx crate. Do I have to add a stm32f103xx crate dependency to my Cargo.toml, or is there a way to re-use the exported definition of stm32f103xx-hal?

Note that this is a different problem from simply "how to use a macro from a different crate". Declaring #[macro_use(interrupt)] on stm32f103xx-hal yields a cannot find macro 'interrupt!' in this scope error.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Michael Böckling
  • 7,341
  • 6
  • 55
  • 76
  • Possible duplicate of [How do I use a macro defined in another crate?](https://stackoverflow.com/questions/39945901/how-do-i-use-a-macro-defined-in-another-crate/39945929#39945929) – ljedrz Apr 26 '18 at 07:46
  • Does the `interrupt!()` macro work if you import `stm32f103xx-hal` like this: `#[macro_use] extern crate stm32f103xx-hal;`? – Jan Hohenheim Apr 26 '18 at 08:17
  • Found some additional discussion about the now defunct "macro_reexport" feature, which seems very related to the issue at hand: https://github.com/rust-lang/rust/issues/29638 – Michael Böckling May 03 '18 at 17:24

3 Answers3

4

I think you need to add stm32f103xx to your Cargo.toml. Here is why:

For example, in the Diesel lib.rs

#[macro_use]
extern crate diesel_derives;
#[doc(hidden)]
pub use diesel_derives::*;

They put #[macro_use] on the extern crate line then they use everything from the diesel_derives project.

In your case the lib.rs looks like that:

pub extern crate stm32f103xx;

So the re-export does not specify the use of macros.

This is why you need to add stm32f103xx to your Cargo.toml in order to specify the use of macros in your own lib.rs.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Kim Desrosiers
  • 744
  • 7
  • 13
  • 1
    That makes sense, though it seems a little brittle to design Cargo like that. Thanks. – Michael Böckling Apr 26 '18 at 14:24
  • @MichaelBöckling what is "brittle" about it? The crate you are using chooses not (or forgot) to export the macros. What do you want Cargo to do about it? Ignore the code as written and just wildly bring in whatever it feels like? – Shepmaster Apr 26 '18 at 15:05
  • 1
    Well, they can forget to export the macros. The crate already decided about its public API surface, why should another crate that re-exports it have any say in the matter? – Michael Böckling Apr 26 '18 at 15:26
  • @MichaelBöckling Because what they choose to re-export becomes part of *their* API surface. – jbg Jun 30 '19 at 15:54
  • 1
    But when the re-exported crate is `optional=true` and comes via feature (and `pub use`), there's no possibility for the re-exporter to `#[macro_use]` it? – OJFord Jan 23 '22 at 14:28
1

The intermediate crate should re-export the macros so that they are available:

mac/src/lib.rs

#[macro_export]
macro_rules! a_macro {
    () => (42);
}

inter/src/lib.rs

pub extern crate mac;
pub use mac::*; // Re-export the macros

ex/src/main.rs

#[macro_use]
extern crate inter;

fn main() {
    println!("Hello, {}", a_macro!());
}

In your case, it's either a bug with the library or they've deliberately decided to not re-export them, so you need to take it up with them. You can choose to directly rely on the underlying crate, but then you open yourself up to having mismatched versions of the crate, leading to annoying errors.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Shepmaster brings a good point here. In your case, even if the crate `stm32f103xx-hal` would re-export macros from `stm32f103xx`, it would be a best practice to import `stm32f103xx` explicitly allowing you to specify the version you need. – Kim Desrosiers Apr 26 '18 at 15:47
  • 1
    @KimDesrosiers I believe you've understood me completely backwards. I'm saying that relying on both the re-exported crate and the crate directly is a **bad** idea, due to the version incompatibilities that will occur. You should only use the re-exported code (which you cannot because it's not currently exporting the macro). It is **not** "a best practice to import `stm32f103xx` explicitly" because you cannot control the version that `stm32f103xx-hal` uses. – Shepmaster Apr 26 '18 at 17:16
  • I read the last sentence too quick my bad. However, maybe it is not a good practice but in the current situation, we don't have the choice if we want to use macros. Is it possible use two different versions of a crate in the same project? I mean can we alias the re-export where needed and use the other one from our Cargo.toml? – Kim Desrosiers Apr 26 '18 at 18:28
  • 2
    @KimDesrosiers if you mean "the current situation" as the state of these two specific crates right now, then yes, you have to include the crate both directly and indirectly, to your own future detriment. That's why I encourage OP to file an issue with the crate(s). AFAIK, you cannot specify two versions of the same crate in a Cargo.toml, but you certainly can have multiple versions of a crate in the entire crate dependency graph. See [Why is a trait not implemented for a type that clearly has it implemented?](https://stackoverflow.com/q/44437123/155423) as one concrete example. – Shepmaster Apr 26 '18 at 19:23
  • Definitively open an issue would be the right thing to do. – Kim Desrosiers Apr 26 '18 at 19:49
  • `pub use mac::*; // Re-export the macros` is re-exporting everything, is there a way to just re-export the macros and nothing else? – AldaronLau May 21 '18 at 20:23
0

This was changed just recently to use the generic import system.

Assuming you want to use the interrupt! macro from the foocrate, the new way to do it looks like this:

#![feature(use_extern_macros)];

pub use foo::interrupt;

Note that this is not yet stable, so it is feature-gated behind use_extern_macros.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Michael Böckling
  • 7,341
  • 6
  • 55
  • 76