Home > OS >  Varying number of generic parameters based on a feature
Varying number of generic parameters based on a feature

Time:07-15

I am using Rust 1.62.0 and would like to add a variant to an enum based on whether a feature is enabled.

The following compiles without issues.

enum MyEnum<#[cfg(feature="my-feature")] T> {
    Always,
    #[cfg(feature = "my-feature")]
    OnlyWithFeature(T),
}

So I thought that all I need to do is to "guard" T with a cfg attribute wherever it occurs. However, the second T in the following seems to be an issue.

impl<#[cfg(feature="my-feature")] T> MyEnum<#[cfg(feature="my-feature")] T> {
    pub fn new() -> Self {
        Self::Always
    }
}

The error messages are as follows.

With no features:

 --> src/main.rs:7:74
  |
7 | impl<#[cfg(feature="my-feature")] T> MyEnum<#[cfg(feature="my-feature")] T> {
  |                                                                          ^
  |
help: expressions must be enclosed in braces to be used as const generic arguments
  |
7 | impl<#[cfg(feature="my-feature")] T> MyEnum<#[cfg(feature="my-feature")] { T }> {
  |                                                                               

error[E0107]: this enum takes 0 generic arguments but 1 generic argument was supplied
 --> src/main.rs:7:38
  |
7 | impl<#[cfg(feature="my-feature")] T> MyEnum<#[cfg(feature="my-feature")] T> {
  |                                      ^^^^^^-------------------------------- help: remove these generics
  |                                      |
  |                                      expected 0 generic arguments
  |
note: enum defined here, with 0 generic parameters
 --> src/main.rs:1:6
  |
1 | enum MyEnum<#[cfg(feature="my-feature")] T> {
  |      ^^^^^^

For more information about this error, try `rustc --explain E0107`.

With --features my-feature:

error: invalid const generic expression
 --> src/main.rs:7:74
  |
7 | impl<#[cfg(feature="my-feature")] T> MyEnum<#[cfg(feature="my-feature")] T> {
  |                                                                          ^
  |
help: expressions must be enclosed in braces to be used as const generic arguments
  |
7 | impl<#[cfg(feature="my-feature")] T> MyEnum<#[cfg(feature="my-feature")] { T }> {
  |                                                                               

error[E0747]: constant provided when a type was expected
 --> src/main.rs:7:74
  |
7 | impl<#[cfg(feature="my-feature")] T> MyEnum<#[cfg(feature="my-feature")] T> {
  |                                                                          ^

For more information about this error, try `rustc --explain E0747`.

I have the impression that the compiler tries to parse T as a const generic, so I guess the cfg attribute doesn't belong there...

Guarding the whole impl block seems to work fine, for example the following compiles.

#[cfg(not(feature="my-feature"))]
impl MyEnum {
    pub fn new() -> Self {
        Self::Always
    }
}
#[cfg(feature="my-feature")]
impl<T> MyEnum<T> {
    pub fn new() -> Self {
        Self::Always
    }
}

However, this would essentially require me to write a big impl block twice.

Is there a simpler way to use a different number of generic parameters based on a feature?

CodePudding user response:

You can use a macro_rules! declarative macro to prevent needing to duplicate code:

enum MyEnum<#[cfg(feature="my-feature")] T> {
    Always,
    #[cfg(feature = "my-feature")]
    OnlyWithFeature(T),
}

// Produces impl blocks for feature enabled and disabled
macro_rules! impl_MyEnum_with_without_feature {
    // Matches `impl MyEnum` followed by a block
    { impl MyEnum $implementations:tt } => {
        #[cfg(not(feature="my-feature"))]
        impl MyEnum $implementations
        
        #[cfg(feature="my-feature")]
        impl<T> MyEnum<T> $implementations
    }
}

impl_MyEnum_with_without_feature! {
    impl MyEnum {
        pub fn new() -> Self {
            Self::Always
        }
    }
}

Of course you will still need to create a separate impl block for anything that needs the generic parameter.

  • Related