# 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(any())'.dependencies] # <- the trick
foo-derive = { version = "=1.2.3" }
[features]
derive = ["dep:foo-derive"]
```
The trick is a target specific dependency with "impossible" `any()` cfg. This cfg is never true, so
`foo` never actually depends on `foo-derive` (unless the `derive` feature flag is enabled). Non the
less, this platform-specific dependency forces Cargo to include foo-derive into the lockfile, so the
"no two semver-compatible versions of a crate" constraint kicks in.
Eg, if the user's tries to do
```
[dependencies]
foo = "=1.2.3"
foo-derive = "=1.2.2"
```
their build will (correctly) fail.
Crucially, because the cfg is never true, the `foo` crate doesn't _actually_ depend on `foo-derive`,
so it can be independently compiled. Similarly, although every lockfile gets a `foo-derive`, it
isn't actually downloaded unless it is needed elsewhere in the crate graph (the situation is
similar to having windows-specific deps in a lockfile of a linux-only crate).
Note that it's important that target-specific deps, rather features, are used here. With features,
Cargo can look at the entires set of features of the root crate being compiled, deduce the _precise_
set of features that could be activated for dependencies, and prune anything which is guaranteed to
not be needed from Cargo.lock.
With target-specific dependencies (`target.'cfg()'.dependencies` syntax), Cargo has to assume that
each `cfg` _could_ be true, and so it has to conservatively include everything into a lockfile.
## Important Clarification
So, does this actually work? I don't know! I don't think anyone tried this hack at scale, so it
might be the case that something breaks HORRIBLY somewhere. We won't know without trying though :-)
And it seems to work in this little experimient (there's a bunch of `macro-dep-test` and
`macro-dep-test-macros` versions published to crates.io, so you could check for yourself )