Declaring optional supportive leaf features

Hi,
when defining features for a Cargo package I questioned how I can define valid feature combinations.

For example lets imagine you have one or more supportive (backend) features, which can be used/consumed within other features. They could provide a service.

Then it's relatively simple to declare a dependency like this:

[features]
service_feature_c = []
consuming_feature_a = ["service_feature_c"]
consuming_feature_b = ["service_feature_c"]

i.e. you can express that
(a OR b) => c (in the case of an optional feature, you probably do not declare it as a dependency)

but you can't declare additional conditions about the inverse direction. i.e. it should not be allowed to enable c without a or b (i.e. c alone does not provide any value without a or b).
c => (a OR b)

Is there a common approach to handle this by somehow disallowing invalid or unsupported feature combinations?

BR

Cargo's features are unified across the entire dependency tree, so they're always a union all features requested anywhere, and aren't allowed to conflict.

You may get both a and b enabled at the same time and you can't prevent that (you could fail compilation, but it may be frustrating or impossible for a user to disable a feature force-enabled by some indirect dependency).

Thanks for the reply.
I know but I don't see how that example I've shown would contradict this. The first one is already possible in Rust and requires to enable service_feature_c if you compile with consuming_feature_a or b.

The second expression says a OR b not a XOR b, i.e. it would allow to compile with:

  • a, c
  • b, c
  • a, b, c
  • EDIT: only a, only b or both without c would be also valid

but would disallow to use c without a or b.

I remember using the compile_error! macro once for a similar situation

I see. Yeah, that would theoretically be possible. I don't think it can be done directly currently.

The closest thing Cargo has is dep?feature syntax that enables feature of dep if the dep is already selected.

c = ["a?c", "b?c"]

but you'd rely on user enabling a or b anyway.

Alternatively, you can programmatically set custom cfg() from your crate's build.rs. You can detect if c is set and set a custom cfg(a) or cfg(b) based on whatever logic you want. This is only usable for conditional compilation, and can't add or remove optional dependencies.