articles

Improvements for #[doc] attributes in Rust

Since Rust 1.54, we can now use function-like macros in attributes. It has a lot of advantages for the #[doc] attribute, let's check some of them!

Using README file as crate documentation

You can do it by simply writing at the top of your lib.rs file:

Run#![doc = include_str!("README.md")]

And that's it! Your README file will now be the crate documentation and the code examples inside it will be tested as well.

One thing to note though: by defaut, code blocks in markdown are considered as rust code blocks. You'll need to annotate them to prevent to have failed tests because the rust compiler fails to compile them. So for example:

Install X lib doing:
```
$ apt install X
```

If you run cargo test with this file, rustc will try to compile $ apt install X, and as you may guess, it won't go well... To prevent this issue, simply mark it as a console/shell/bash (or whatever else which isn't rust):

Install X lib doing:
```console
$ apt install X
```

Another thing to note: it would a good idea to annotate your rust code blocks as rust language to be sure that it has the correct syntax highlighting when browsing the rendered README:

A rust example:
```rust
println!("hello!");
```

Of course, you can include any markdown file in any doc comment just like that. If you have a markdown file in a sub-folder for a given module, simply include it the same way (the path of the markdown file is relative to the current source file).

Using doc comments to extend tests capabilities

Unit tests in Rust are nice, but also limited. Interestingly enough, rustdoc tests are more flexible. For example, you wrote a (proc?) macro and want to ensure that it cannot compile if it receives a given input. If you try to do that using #[test], it'll be quite tricky. With rustdoc, not an issue anymore! Let's you put your macro test in a compile_fail.rs file, then you can just use:

Run/// ```compile_fail
#[doc = include_str!("compile_fail.rs")]
/// ```
mod doc_test {}

And that's it! Then when running cargo test, it'll check if your code is failing compilation as expected.

Similarly, you can use should_panic if you want to check that the code is panicking. If you want more information about documentation tests, please take a look at the rustdoc book.

Little reminder about cfg(doctest)

We just talked about documentation tests, but I didn't mention cfg(doctest). Just like the compiler is providing cfg(test) when you run rustc --test (through cargo test), the same goes for rustdoc! When you run rustdoc, it provides cfg(doc), and when you run rustdoc --test (through cargo test as well) it provides cfg(doctest).

Let's get back to our code example: we don't want it to appear into the documentation or to be included when we're compiling, only when we are testing documentation examples. To do so, use cfg(doctest):

Run#[cfg(doctest)]
/// ```compile_fail
#[doc = include_str!("compile_fail.rs")]
/// ```
mod doc_test {}

Conclusion

That's it for this short (but hopefully useful!) blog post. If you want to learn more on "how to write documentation for a Rust crate", I recommend you to take a look at this blog post.

Posted on the 03/08/2021 at 13:30 by @GuillaumeGomez

Next article

sysinfo: how to extract systems' information

Previous article

Interacting with data from FFI in Rust
Back to articles list
RSS feedRSS feed