Something of a footgun with crate features

Just writing this down in case it helps someone else – this is just a consequence of Cargo's build model, but it was unintuitive to me at least:

Consider a (virtual) Cargo workspace with members = ['core', 'extra']:

  • core has a non-default feature foo
  • extra depends on core with features=["foo"].

Now, if I execute cargo test --workspace with no features enabled, Cargo nevertheless builds core with foo because extra enables that feature in core.

I was trying to make sure that core compiles and the relevant tests run even without foo, but unknowingly I wasn't actually testing that at all! I was really baffled when I realized that the foo feature somehow gets enabled even when I thought it wouldn't, and it took me a while to realize what was going on.

1 Like

The IMO even worse features footgun is: You write a crate foo that depends on crates bar and baz. And bar depends on baz with feature feat. Now your crate foo also makes use of API behind baz/feat. But you don’t notice because bar activated that feature for you. But bar’s dependency on baz is a private dependency (just for functionality, no publicly used / re-exported types). So one day bar decides to change up its dependency on baz (e.g. updating to a different version of baz, or removing the feature feat because they no longer need it or noticed they never needed it in the first place) and suddenly your crate foo breaks, and you’re even to blame for that.

6 Likes

One thing to note is that it can depend upon which directory in the workspace you run cargo build from.
So if you are in a subdirectory of the workspace, where the dependency graph does not enable features="foo", you can get build failures if it doesn't depend upon anything that enables it.

But when building from the top-level of the workspace, it can be built with the feature enabled if there are other siblings that depend upon it. That is potentially a way to test your feature isn't depended upon in CI once you know about it.

Edit:
Perhaps in addition to that you could make some tool similar to the cargo tree -e features which errors if the feature gets enabled in the direct dependency graph.

4 Likes

The --package option has the same effect.

3 Likes

For testing features in workspaces without tripping over this, I recommend cargo-hack. If you just run cargo hack --workspace --feature-powerset test, then each package in the workspace will be tested separated, and each package that has multiple features will be tested with each possible feature combination.

8 Likes

Ah, awesome. I've been wondering if something like that exists, but been too lazy to search. Thanks!