When developing procedural macros, it's a common pattern that you need to access some accessory types or names from the related "runtime" crate. This is commonly done simply by assuming some fixed crate name. For example, in case of ref-cast
, macros assumes that runtime crate will be ref_cast
.
This works most of the time, however, it's not ideal (although, it's worth mentioning that 99% of the time it works exactly as designed):
- If runtime crate is renamed via
Cargo.toml
for whatever reason, code will stop compiling:
# This will break `#[derive(Fail)]` functionality!
success = { package = "failure", version = "0.1.6" }
For macro_rules, there is a $crate
metavariable which always points to the defining crate.
- Sometimes, there is a need to hide somebody's else procedural macro behind your own abstractions.
For example, here
inventory crate is used inside gflags
crate; inventory
crate needs ctor
crate and to avoid forcing everyone to have an explicit dependency on ctor
, inventory
re-exports ctor
from inventory
itself. However, when inventory
is used internally by gflags
, the inventory
is no longer a direct dependency of gflags
users so ::inventory::ctor
will no longer compile. To solve that, inventory
supports a special attribute telling it which prefix to use.
However, most of the procedural macros don't support anything like that since it's not anticipated that they are going to be used from other crates or that their "runtime" crates might be renamed.
Question
The question I have is if it would be possible to define some magic $crate
metavariable, but for procedural macroses? So, every time procedural macro needs its dependencies it can generate code like:
quote! {
// We don't really know our "runtime" crate name, but that's okay!
use $crate::internal::SomeImportantTrait;
}
Here is how I think it might work. Oftentimes, procedural macroses are re-exported from the "runtime" crate, like this:
pub use ref_cast_impl::RefCast;
What if there was an attribute that would allow to change what $crate
would expand via an attribute on a use statement. Like this:
// This would make `$crate` generated by `ref_cast_impl::RefCast` to be replaced by `ref_cell`
#[crate = ref_cell]
pub use ref_cast_impl::RefCast;
Or like this:
// This would make `$crate` generated by `ref_cast_impl::RefCast` to be replaced by whatever name this crate has in code where `derive(RefCell)` is used.
#[crate = crate]
pub use ref_cast_impl::RefCast;
Or even like this:
// Now I'm re-exporting it again, and this time I'm making the target macro to produce `<this crate>::ref_cast` in place of `$crate`!
#[crate = crate::ref_cast]
pub use ref_cell::OurRefCast;
// Re-export all ref_cast runtime needs so `OurRefCast` can reference them.
#[doc(hidden)]
pub use ref_cast;
And the default behavior could be #[crate = crate]
, i.e, when you re-export procedural macro, it's $crate
is "bound" to the crate doing the re-exporting.
Is it possible? Does it make any sense?