What is #[cfg_attr]
?
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.
Realâworld examples
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:
(Multiple #[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.