#[cfg] on where clause items?

Is there any way to get, or emulate the functionality, of a #[cfg] on a where clause? As in,

impl<A, B, C> SomeTrait for Foo<A, B, C>
where
    #[cfg(some_a)] A: SomeOtherTrait1,
    #[cfg(some_b)] B: SomeOtherTrait2,
    #[cfg(some_c)] C: SomeOtherTrait3,
{
    // ...
}

I'm aware it can be done by creating multiple impls and gating the entire impl behind a #[cfg] but that doesn't scale well to situations where you have multiple combinations of #[cfg] for each potential where clause.

I can think of a really hacky solution that would work by introducing intermediate traits that are conditionally required.

// these are the traits from upstream that we care about
trait SomeOtherTrait1 { ... }
trait SomeOtherTrait2 { ... }
trait SomeOtherTrait3 { ... }

// these are the hacky intermediate traits
cfg_if::cfg_if! {
  if #[cfg(some_a)] {
    trait Requirement1: SomeOtherTrait1 { }
    impl<T: SomeOtherTrait1> Requirement1 for T { }
  } else {
    trait Requirement1 { }
    impl<T> Requirement1 for T { }
  }
}
// (copy/paste for SomeOtherTrait2 and SomeOtherTrait3)

// The actual impl block
impl<A, B, C> SomeTrait for Foo<A, B, C>
where
    A: Requirement1,
    B: Requirement2,
    C: Requirement3,
{
    // ...
}

There are a bunch of issues with this (we might need to expose Requirement1 to the user, it doesn't scale, etc.), but I think the biggest problem will be that you still need to wrap things that make use of A's SomeOtherTrait1 implementation with #[cfg(some_a)]... I dunno, I guess it could work if you are really stuck :man_shrugging:

That said, if it were me I'd try to refactor the code so it abstracts over the conditional compilation. For example, you might have the "normal" implementation for SomeOtherTrait1 and a dummy implementation which always just errors out with Err(Error::Unsupported), and a top-level create_some_other_trait1() -> impl SomeOtherTrait1 which instantiates the "real" or "dummy" versions depending on the conditional compilation. That way you can still use the same impl block, but sometimes things may fail at runtime if that support for that functionality wasn't compiled in.

It would, probably, work, but keep bug #90292 in mind, it's really annoying in such situations.

You can always use proc_macro to filter-out certain parts of trait definition.

That said, if it were me I'd try to refactor the code so it abstracts over the conditional compilation.

Unfortunately not really an option in this case, since I need to support both situations rather than erroring out at compile time or, worse yet, runtime. I've tried alternatives and they get very ugly to maintain safely pretty fast.

You can always use proc_macro to filter-out certain parts of trait definition.

This is actually already in a proc macro :sweat_smile:, and I'm trying to avoid having to write a generator for a combinatorial superset of multiple #[cfg] attributes.

I can experiment with the intermediate trait option. One thing that occurred to me is that you can use macros in type positions, so in theory I could write a macro in the form of

where test_some_a!(A): SomeRequirement

and have test_some_a!() map to either the type itself or some dummy that trivially satisfies SomeRequirement. Still ugly though. I'm tempted to pre-RFC something that would allow #[cfg] on where clauses.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.