Associated Proc Macro Pattern

It's a common pattern to provide a foo crate with trait definitions, and foo-derive crate with a proc-macro derive implementation. Typically, you want foo and foo-derive to be versioned in lockstep, because derive crates like to use #[doc(hidden)] non-semver-guarded API. Usually, this is solved by a derive feature, which makes foo depend on foo-derive with =x.y.z constraint.

This, however, is problematic for compile times! It means that compilation of foo-derive is sequenced before compilation of foo. As foo-derive is a derive macro, it needs to parse the Rust language. Rust is not a small language, so parsing it is fundamentally hard, and requires loads of code to do correctly. So it takes some time to compile foo-derive. What's worse, while normally Cargo pipelines compilation such that .rmeta files are all that's needed to unblock compilation of dependent crates, for proc macros Cargo really needs to link the whole .so!

The bottom line, while

toml foo = { version = "x.y.z", features = ["derive"] }

is easy to explain and works correctly, it could significantly reduce the amount of parallelism available during builds.

On the other hand, while

toml foo = { version = "x.y.z" } foo-derive = { version = "x.y.z" }

provides better compilation time, it doesn't constrain foo and foo-derive to be the same version.

The pattern in this crate shows how to add that constraint! We can use the following declaration of dependencies in foo's Cargo.toml:

```toml [package] name = "foo" version = "1.2.3"

[dependencies] foo-derive = { version = "=1.2.3", optional = true }

[target.'cfg(all(tobe, not(tobe)))'.dependencies] # <- the trick foo-derive = { version = "=1.2.3" }

[features] derive = ["dep:foo-derive"] ```

The trick is a target specific dependency with "impossible" cfg. This cfg is never true, so foo never actually depends on foo-derive (unless the derive feature flag is enabled). Non the less, be