It’s just like
#[cfg], but for attributes. Just as
#[cfg(condition)] allows you to only compile the thing it decorates if the condition is true,
#[cfg_attr(condition, attribute)] allows you to only add the attribute to the thing it decorates if the condition is true. That is, if the condition is true, it’s equivalent to
#[attribute], and if the condition is false, it vanishes into thin air.
Enabling nightly features in certain situations
In anymap, I have a
nightly Cargo feature which, if enabled, implements some additional features and performance optimisations (like using a more efficient hasher on the hash map) that are not yet possible on stable rustc. Those require a couple of extra gated features, but I only want the
#![feature(core, std_misc)] crate attribute to exist if the
nightly Cargo feature is enabled. I can do that:
This might also be handy for things like tests:
Special attributes for bootstrapping rustc
A number of times I have noticed duplicated items, one
#[cfg(stage0)] and one
#[cfg(not(stage0))], differing only in attributes; this is not necessary. I’ll take a current example:
Bonus: doc comments are truly sugar
Dynamic doc comments is a non‐obvious area in which
#[cfg_attr] can be used.
First of all you must understand that these two are precisely the same in all ways that matter, because the former desugars to the latter during parsing:
#[doc] attributes are fine; they are joined together with line breaks between to form the final doc comment.)
This can be combined with
#[cfg_attr] to allow conditional documentation, like adding a part to the documentation if a feature is enabled:
When built, the documentation will then announce whether it was built with the
foo feature enabled or disabled, which might be handy. And a new section will be added if the
bar feature is enabled, or a note on what you’re missing out on and maybe how to enable it if you don’t have it enabled. There, four versions of the documentation without the need to duplicate even a single vast swathe of code!
Note also how when we manually write
#[doc] attributes we put a single leading space at the start of the string; this is because
/// Foo desugars to
#[doc = " Foo"]; if all the lines start with a space, it will be trimmed from them all, but if not then such lines will have a leading space, which might mess up your Markdown formatting of things like headings marked with leading hashes.
I will admit that dynamic doc comments isn’t going to be completely useful everywhere, for useful applications of it aren’t going to be very common and if people use only one centralised documentation source they’ll miss out on getting the special version that just suits their use case, but as we get better tooling I think it might become more appropriate. And even if it’s not so very useful (hey, it’s bonus material, it doesn’t need to be so very useful), it’s definitely fun.
Super bonus: adding macros into the mix!
Imagine that the trait bounds need to be changed if the
bar feature is enabled. Argh! you say. Back to the drawing board! Not so—you can save all the duplication with a simple macro that, given an item and appropriate attributes, dumps them out with the documentation attributes added. Here’s a simple example:
Voilà! Whichever version of the
Traitor trait we get, it has the right documentation on it.