Building app with optional features returns an error as if feature is required

Hi everyone !

I want to split some of my app functionality into optional features. So, I added them in Cargo.toml:

[features]
default = ["ui"]
ui = ["crossterm/default", "tui/crossterm", "crossterm/event-stream"]
console = []

My [dependencies] contains this:

[dependencies]
// ...
tui = { version = "0", features = ["crossterm"], optional = true }
crossterm = { version = "0.27.0", optional = true }
// ...

and in the code I added condition checker:

let mut features: Vec<Box<dyn Feature>> = vec![];
cfg_if! {
    if #[cfg(feature = "ui")] {
        use crate::features::ui::UI;

        let ui = UI::new(query_sender.clone(), query_receiver.clone());
        features.push(Box::new(ui));
    }
}

cfg_if! {
    if #[cfg(feature = "console")] {
        use crate::features::console::Console;

        let console = Console::new(query_sender.clone(), query_receiver.clone());
        features.push(Box::new(console));
    }
}

but I got a lot of errors on compile like next error:

error[E0433]: failed to resolve: use of undeclared crate or module `crossterm`
   --> src/features/ui/mod.rs:100:41
    |
100 | ...                   let crossterm::event::KeyEvent { modifiers, code, .. } = key;
    |                           ^^^^^^^^^ use of undeclared crate or module `crossterm`

So, it seems the ui feature directory ignores the fact I import it only optionally.
The way how I build the app:

cargo build --no-default-features --features console

Could somebody point me into right direction on this feature ? Is it possible to avoid errors without putting cfg_if! into each file ?

You want to add a #[cfg(...)] to the module declaration, i.e. immediately before wherever mod ui; is.

2 Likes

In my src/features/ui and src/features/console I have the module directories. This is src/features/mod.rs:

pub mod ui;
pub mod console;

I tried this in src/features/mod.rs:

#[cfg(feature = "ui")]
pub mod ui;

and after that I got an errors like:

error[E0433]: failed to resolve: could not find `ui` in `features`
  --> src/primary/client/realm/realm_split.rs:10:14
   |
10 |     #[derive(WorldPacket, Serialize, Deserialize, Debug)]
   |              ^^^^^^^^^^^ could not find `ui` in `features`

Also I tried this in src/features/ui/mod.rs:

#[cfg(feature = "ui")]
pub struct UI {
    _receiver: BroadcastReceiver<HandlerOutput>,
    _sender: BroadcastSender<HandlerOutput>,
}

or even put this #[cfg(feature = "ui")] at the most top of the file. I still have an errors about undeclared crates.

P.S. if this could help, this is the branch where I work on this task: tentacli/src/primary/client/mod.rs at IC-64 · idewave/tentacli · GitHub

My fall. The issue was with ui import in macros.

Thank you !

Take this as a lesson in Rust design: getting cfgs right is hard, and has to be tested by compiling the program both with and without the feature enabled.

Because of this, before defining a feature, you should think about whether it's really necessary and whether the goal could be achieved another way (like having an entirely separate library containing the optional code). And, if you do decide to define a feature, arrange it so that not very many cfgs are required, because every one of them is an opportunity for a mistake.

2 Likes