Help stabilize a subset of Macros 2.0!

I already expressed my concern about stabilising the use-based macro importing in GitHub ( https://github.com/rust-lang/rust/issues/35896#issuecomment-378495910 ), but I want to rehash it here.

With the new use-based import, one is able to selectively import just one macro, which is great. However, because the macro_rules! macros don’t have hygiene with regard to other macros, all the macros the imported macro expands to also need to be in the caller’s scope. Expanding to other macros that are considered implementation details is actually a quite common thing to do, so I’m concerned of the ergonomics of the use-based importing, especially if #[macro_use]is going to fade away together with the extern crate syntax.

An example: To use slog's info macro, I imported it like this: use slog::info;, but after that, I had to import also these macros for it to work: use slog::{o, kv, log, record, b, record_static};. This makes the new syntax, as nice it is, actually ickier to use than the old one – something that needs to be considered if we actually want people to move using the new syntax.

I was told that as this is how macro_rules! works, there’s not much we can do about it. However, I keep thinking that wouldn’t it be possible to keep backwards compatibility while enabling a kind of plan b:

If a macro_rules! macro is imported using the new use mechanism AND the normal expansion fails with “cannot find macro foo! in this scope” error, it tries to expand it again AS IF the macros from the crate the expanding macro originates from, were in scope. (That is, as if the there would have been a #[macro_use] attribute.)

Why this is good:

  • It behaves like it used to be when using the old import mechanism; new functionality is only enabled with the new use style importing.
  • It behaves like it used to be when the macro expansion succeeds, so it’s backwards compatible with code that compiles at the moment.
  • It doesn’t import all of the macros wholesale like #[macro_use] does, as they are considered only inside macros, when the normal expansion fails.
  • It enables the use of the new kind of syntax, without the use slog::{o, kv, log, record, b, record_static}; kind of ergonomics downsides.
  • These rules would apply only for macros_rules! macros, so in the future, macro macros could, and should be actually hygienic.

Downsides:

  • There might be macros that expect some other macros to be imported from other crates by the users. This scheme doesn’t magically help in this case. (The user still needs to import the other macros so nothing changes, though.) I don’t expect this to be a common case, though.
  • It doesn’t make macro_rules! macros actually hygienic, that’s something that can be hardly changed backwards compatibly. It just makes importing them easier and cleaner.
  • I’m unaware of how much of churn in the compiler this would require.
4 Likes