Compile new version of library for each dependency - feature flags additive behaviour

I am currently trying to write a binary crate as a wrapper for a library, which sets constants based on feature flags that feature flag set of the package taking it as a dependency.

My initial thought was to create three intermediary packages for each of the feature flags, and then call those, however, I now understand that these features are additive, and so it will simply set the constant to be whichever cfg! statement evaluates first.

My question is therefore, is there a way to compile this shared dependency three times for each of the intermediary packages, therefore only setting one of the features at a time? Or, is there another way to get around this?

There is no way to duplicate a dependency without making a copy of the source code and declaring it to be a different package.

It sounds like the library is misusing features for a configuration that would be better suited for a function parameter (or const generic, if it really needs to be constant). I say this not just because it is not working for you, but because it is failing to have additive features. "Additive" isn't referring to how Cargo combines feature sets, but to the idea that activating a feature should never remove functionality that was present without it (in this case, operating with a different value for the constant than that feature requests).

The best path forward would be to patch the library to use const generics or regular function parameters.

3 Likes

Thanks for your response, that does make a lot of sense, and reinforces what I was lead to believe from my own reading around.

I'm trying to wrap my head around how it should be done in a rust way, but struggling to understand how it would work. To give a bit more detail, the features are used to determine a security level, which then influences other constants - moving these to parameters or const generics would mean that (nearly) every function would need an extra 10-11 parameters.

I have thought about perhaps using structs, but I don't believe that structs support constant values (due to the nature of the values I believe it best to keep them as constants), but can't think of any alternatives.

They do.

2 Likes

If one "security level" sets all the other parameters, those can be expressed as const fns that take the security level as an argument and compute the derived information. So, only the security level need be passed around, whether as a const generic or a regular function argument.

1 Like

Here's a way to pass multiple const parameters via a single generic:

trait Config {
    const PRINT_AT_ENTRY: bool;
    const NUM_LOOPS: usize;
}

fn do_something<Cfg: Config>() {
    if Cfg::PRINT_AT_ENTRY {
        println!("enter");
    }
    for _ in 0..Cfg::NUM_LOOPS {
        println!("loop");
    }
}

struct MyConfig;

impl Config for MyConfig {
    const PRINT_AT_ENTRY: bool = true;
    const NUM_LOOPS: usize = 2;
}

fn main() {
    do_something::<MyConfig>();
}
1 Like

There is also the question of whether this needs to be a constant and defined at compile time at all.

Maybe you could achieve the same thing using a static variable, some sort of set_security_level() function, and getters?

A direct way to answer the task in the OP is "compile the binary 3 times, once with each configuration."

From the followups, though, it sounds like you want all 3 in the same binary, so the const generics is the way to go.

Thanks for the answers everyone! Still fairly new to rust, so enjoying these new ways of thinking about everything. In the end, I have made changes so that it'll be:

  • constants for true constants
  • struct containing security level variables
  • struct methods to get other parameters that depend on security level variables

So far, I believe that this is the most elegant, readable and consistant approach, but of course, if there is anything glaringly obviously wrong with how I have done this then please let me know :smiley:

1 Like

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.