IntelliJ Rust: New Functionality for Cargo Features

Like C/C++ and other native languages, Rust includes support for conditional compilation. The most common way to instruct the compiler whether to include or ignore a piece of code in compilation is to add a cfg attribute with the required condition. For example, you can use this mechanism to check the target architecture and switch code blocks at compile-time depending on the operating system.

IntelliJ Rust detects conditionally disabled blocks in your project and excludes them from the codebase. This means that name resolution and analysis ignore those pieces, so you won’t get errors and warnings inside them, and no items from that code will appear elsewhere in auto-completion. Also, the plugin grays the disabled blocks out in the editor.

This level of cfg support has been available in the plugin for a long while, but something was missing. Conditional options can include Cargo features. Previously, IntelliJ Rust supported only the features declared in the project’s dependencies, but now your workspace features are supported too.

We’ve implemented a smart UI to make your work with features more transparent: you can enable or disable any specific feature of your workspace right inside a Cargo.toml file. Name resolution and code insight will take that into account.

Let’s examine in detail how IntelliJ Rust handles Cargo features when they are used across various levels of dependency.

Library features

The simplest case is when you need to exclude features from a library, or include only some of them in your build.

To start, let’s add the tokio crate and enable all of its features using the full option:

tokio = { version = "0.3.0", features = ["full"] }

If we open tokio’s Cargo.toml and navigate to the [features] section, we’ll see the selected checkboxes next to all the features listed in full:

Dependency on external crate's features

Now we can specify a particular feature instead of full, for example, signal. In tokio’s Cargo.toml, the checkboxes’ state will change accordingly. Only signal and the features it depends on are enabled now:

Dependency on external crate's features

Please note that in the case of external dependencies, this UI is intended to help you grasp the state of features but not to control it, so the checkboxes here in the library’s Cargo.toml are not clickable. If you need to change the list of features, keep using your project’s .toml config.

Independent package features

Another case is when you create your own features inside a package and use them in cfg blocks. Unlike with external libraries, here you can control the list of features by clearing and selecting the corresponding checkboxes.

Usually, Cargo features are additive and don’t conflict when enabled all together, which is the reason why IntelliJ Rust enables workspace features by default. However, you may want to toggle them on and off, and the plugin’s UI will help you do that!

Additive custom features

The ability to toggle workspace features is especially handy when you use them as switchers instead of additive instances (for example, the Amethyst library has features controlling which of the available rendering engines is invoked at the moment). Previously, you would have had to manually remove the unneeded features from your Cargo.toml in order to turn them off.

As the UI now allows you to easily switch between features, be careful not to enable any mutually exclusive ones simultaneously. The plugin will not prevent you from doing so and will not show a warning, because at the .toml file level there’s not enough information for the plugin to detect potential problems in the sources.

Below you can see a multiple resolve situation reported in the sources when non-additive features are enabled at the same time:

Non-additive custom features

Package features that depend on each other

A slightly trickier case is when your features depend on each other:

wrapper = ["calc"]
calc = ["core"]
core = []

If you toggle one of them on or off, IntelliJ Rust will recognize the dependencies and toggle the related features accordingly.

Dependent custom features

Features with cross-crate dependencies

Your package features can also depend on features from another crate. Let’s make core from the previous example dependent on the macros feature from tokio:

core = ["tokio/macros"]

Switching into tokio’s Cargo.toml, we’ll notice how macros, which is disabled by default, gets enabled when we toggle core on and gets disabled again when we turn core off:

Custom features dependent on external features

Features in a workspace

A more complicated scenario is when features are used in a workspace consisting of several packages.

As a simple example, let’s create a workspace with two packages – bar and foo. In foo’s Cargo.toml, we can declare a dependency on bar and specify that it uses a feature from bar called bar_feature:

Features in a workspace

When using a workspace, you may want to develop a single crate inside it like an independent instance. In this case, you’ll need control over the crate’s features, with the ability to disable those you don’t need at a certain point.

So what happens if you focus on bar and intentionally disable bar_feature? As a result, all the code in foo that uses it will not be resolved, and it might be tricky to find the reason for the errors you’ll get in foo.

To help you avoid these problems, IntelliJ Rust checks the required features in your workspace dependencies and suggests quick-fixes for them:

Quick-fix for workspace features

Finally, your workspace crates can have features that depend on each other. For example, foo_features in foo depending on bar_feature from bar:

Dependent workspace features

Here when we disable bar_feature, the plugin disables foo_feature automatically. However, when we turn bar_feature back on, foo_feature will not get enabled, so you need to roll back the dependent features manually.

In the future we plan to add navigation and completion for features from both .toml and source files, and start supporting cfg_attr. In the meanwhile, we hope you find these new capabilities helpful, and we really want to hear your feedback! Please share your experience of the new UI and let us know how you would like support for Cargo features in IntelliJ Rust to improve.

Thank you!

Your Rust team
The Drive to Develop

image description

Discover more