Features Examples
The following illustrates some real-world examples of features in action.
Minimizing build times and file sizes
Some packages use features so that if the features are not enabled, it reduces the size of the crate and reduces compile time. Some examples are:
syn
is a popular crate for parsing Rust code. Since it is so popular, it is helpful to reduce compile times since it affects so many projects. It has a clearly documented list of features which can be used to minimize the amount of code it contains.regex
has a several features that are well documented. Cutting out Unicode support can reduce the resulting file size as it can remove some large tables.winapi
has a large number of features that limit which Windows API bindings it supports.web-sys
is another example similar towinapi
that provides a huge surface area of API bindings that are limited by using features.
Extending behavior
The serde_json
package has a preserve_order
feature
which changes the behavior of JSON maps to preserve the
order that keys are inserted. Notice that it enables an optional dependency
indexmap
to implement the new behavior.
When changing behavior like this, be careful to make sure the changes are SemVer compatible. That is, enabling the feature should not break code that usually builds with the feature off.
no_std
support
Some packages want to support both no_std
and std
environments. This is
useful for supporting embedded and resource-constrained platforms, but still
allowing extended capabilities for platforms that support the full standard
library.
The wasm-bindgen
package defines a std
feature that
is enabled by default. At the top of the library, it
unconditionally enables the no_std
attribute. This
ensures that std
and the std
prelude are not automatically in scope.
Then, in various places in the code (example1,
example2), it uses #[cfg(feature = "std")]
attributes
to conditionally enable extra functionality that requires std
.
Re-exporting dependency features
It can be convenient to re-export the features from a dependency. This allows
the user depending on the crate to control those features without needing to
specify those dependencies directly. For example, regex
re-exports the
features from the regex_syntax
package. Users of regex
don’t need to know about the regex_syntax
package,
but they can still access the features it contains.
Vendoring of C libraries
Some packages provide bindings to common C libraries (sometimes referred to as
“sys” crates). Sometimes these packages give you the choice to use the
C library installed on the system, or to build it from source. For example,
the openssl
package has a vendored
feature which
enables the corresponding vendored
feature of openssl-sys
. The
openssl-sys
build script has some conditional logic which
causes it to build from a local copy of the OpenSSL source code instead of
using the version from the system.
The curl-sys
package is another example where the static-curl
feature causes it to build libcurl from source. Notice that
it also has a force-system-lib-on-osx
feature which forces
it to use the system libcurl, overriding the
static-curl setting.
Feature precedence
Some packages may have mutually-exclusive features. One option to handle this
is to prefer one feature over another. The log
package is an example. It
has several features for choosing the maximum logging level at
compile-time described here. It uses cfg-if
to choose a
precedence. If multiple features are enabled, the higher “max”
levels will be preferred over the lower levels.
Proc-macro companion package
Some packages have a proc-macro that is intimately tied with it. However, not all users will need to use the proc-macro. By making the proc-macro an optional-dependency, this allows you to conveniently choose whether or not it is included. This is helpful, because sometimes the proc-macro version must stay in sync with the parent package, and you don’t want to force the users to have to specify both dependencies and keep them in sync.
An example is serde
which has a derive
feature which
enables the serde_derive
proc-macro. The serde_derive
crate is very
tightly tied to serde
, so it uses an equals version
requirement to ensure they stay in sync.
Nightly-only features
Some packages want to experiment with APIs or language features that are only
available on the Rust nightly channel. However, they may not want to require
their users to also use the nightly channel. An example is wasm-bindgen
which has a nightly
feature which enables an
extended API that uses the Unsize
marker trait that
is only available on the nightly channel at the time of this writing.
Note that at the root of the crate it uses cfg_attr
to enable the nightly
feature. Keep in mind that the feature
attribute
is unrelated to Cargo features, and is used to opt-in to experimental language
features.
The simd_support
feature of the rand
package is another example,
which relies on a dependency that only builds on the nightly channel.
Experimental features
Some packages have new functionality that they may want to experiment with,
without having to commit to the stability of those APIs. The features are
usually documented that they are experimental, and thus may change or break in
the future, even during a minor release. An example is the async-std
package, which has an unstable
feature, which gates
new APIs that people can opt-in to using, but may not be
completely ready to be relied upon.