Thanks!
X
Paypal
Github sponsorship
Patreon

articles

cfg(doctest) is stable and you should use it

I arrive a bit late considering that #[cfg(doctest)] is stable since rust 1.40 but I think
it's important for people to know about this feature and how to use it.

What is it?

First things first, what is this feature about and when is it set?

The answer: when running rustdoc --test (or cargo test on the doc subpart).

Why use it?

Now, why you should care about it: it allows you to run some checks specifically when
rustdoc is run in test mode. For example, you want to test code examples in a file called
some_file.md. You can now add in your code:

Run#[cfg(doctest)]
macro_rules! doc_check {
    ($x:expr) => {
        #[doc = $x]
        extern {}
    };
}

#[cfg(doctest)]
doc_check!(include_str!("some_file.md")));

What's going on in this code? We import the content of some_file.md as a doc comment upon the
extern block. Which means if it contains rust code blocks, they will be tested on cargo test
runs. However, thanks to #[cfg(doctest)], they will be only included on those tests run, meaning
they won't exist on non-test builds/runs.

The only issue here is that we have to go around some limitations around the #[doc = ...] tag
which doesn't accept to receive something else than a string (this is why we do it through a
macro).

I think I don't repeat it enough but having code examples in your documentation is a huge help for
everyone, including yourself! And if in addition to that, they actually are tested, it means
that it'll prevent the examples to be outdated or incorrect.

Write documentation for yourself and everyone will benefit from it! (I think I should try to
make that quote famous)

Any way to do the same better?

Actually yes! I wrote the doc-comment crate a while ago to make this a bit easier:

Run#[cfg(doctest)]
doctest!("some_file.md");

It isn't a revolution in itself but at least it makes the code reading a lot easier. Another big
advantage is that the doc-comment dependency will only be downloaded and used in case
of test runs. You can add the dependency under the [dev-dependencies] section in your
Cargo.toml file as follow:

[dev-dependencies]
doc-comment = "0.3"

Any drawback?

There is currently a known issue when you try to use an element that is behind #[cfg(doctest)]. For example:

Run//! Hello
//!
//! ```
//! use crate::bar;
//! bar();
//! ```

#[cfg(doctest)]
pub fn bar() {}

If you try to run cargo test, it'll fail because the function bar cannot be found inside the doc test. The problem comes from the fact that the crate isn't compiled with the doctest cfg before running doc tests. So for now, it's not possible.

Conclusion

It wasn't much but I think it's an interesting plus-side of the rustdoc tool. I think not much
people knew about this feature and I hope it'll make it a bit more well-known to our users.

See you around!

Posted on the 07/03/2020 at 22:00 by @GuillaumeGomez

Previous article

Guide on how to write documentation for a Rust crate

Next article

New sysinfo release: time to extend APIs
Back to articles list
RSS feedRSS feed