Summary
Add a new target called [macros]
to the cargo manifest that creates a procedural macro library target inside the crate. Allowing you to create, use, and export procedural macros without creating additional crates or needing a cargo workspace.
Context
With the stabilisation of attribute and function procedural macros, authors are writing and using more and more macros as part of their APIs.
Currently if you as a library author want to provide a derive macro implementation for your trait, or a compile time version of your API (e.g. we wanted to build our own std::env::var
and env!
) you have to convert your project into a cargo workspace, which means you'll have two separate peer crates (e.g. env
and env-macro
).
A common pattern has also emerged of exporting the proc macros through a top level crate (e.g. serde = { version = "*", features =["derive"] }
). When you write a compile time version of your API you'll usually want to make a subset of your API available to both the proc macro and the top level crate so this requires three separate crates (e.g. unic-locale
, unic-locale-impl
, and unic-locale-macros
).
Publishing 2–3 separate crates on crates.io can be a lot of small maintenance work, especially compared to just one crate. It would be nice if instead of having to specify proc-macro = true
in [lib]
, we had a [macros]
target that allowed for specifying procedural macros from inside a single crate.
Configuration
Macro specific dependencies can be specified with the [macro-dependencies]
.
Having a macros target also enables a "macros"
feature flag.
Manifest example
[macros]
# Path to macro crate root.
# Default: `src/macros.rs` or `src/macros/lib.rs`
path = "src/macros.rs"
Importing in Rust
The new macros
target can be imported from the crate root as macros
(e.g. use macros::{Deserialize, Serialize};
)
use macros::{Deserialize, Serialize};
// You can publicly re-export macros with pub use.
pub use macros::{Deserialize, Serialize};
#[cfg_attr(feature = "macros", derive(Serialize))]
struct Point {
x: u32,
y: u32,
}
Unresolved Questions
- Are there other options that could/should be added to a macros target?
- In addition to the macros feature flag should there be a
macros
key in dependencies? This would be harder to mistype as cargo can warn on an unused key, but not on an unused feature. E.g.
serde = { version = "*", features = ["macros"] }
# With macros key
serde = { version = "*", macros = true }