I realized that one of my favorite Rust features doesn’t have a blog post highlighting it [0], so I thought I’d talk a bit about documentation comments and doc tests in Rust.
I’ll give you a quick example. Let’s say you have some code:
There’s a cargo
command to generate documentation from that code:
Which results in automatically generated documentation that looks like this:
In any other language before learning Rust, I would have been pretty impressed at this point [1]. The fact that documentation can be generated with a single command with the default toolchain, no added components? Pretty good already. But Rust lets you take it several steps further with documentation comments.
You already saw the code comment in the screenshot indicating what file I was referring to. That looked like this:
Rust also supports a triple slash comment like this:
That third slash is not just decorative. Let’s rerun cargo doc
and refresh our generated documentation:
Et voilà! The description has appeared in the documentation. And that’s not all. You can even annotate individual fields.
And clicking through to the detail view of the struct gives you the description as well as annotations on the fields:
Ah, but that’s (still) not all. Rust’s documentation tooling solves yet another problem. Let’s say you have an method on Puppy
like so:
You might want to document it in a code comment like so:
But let’s say that whos_good
ended up changing for some reason, for example if Puppy
was refactored to have a first_name
and last_name
instead ()
Aside: Falsehoods Programmers Believe About Names is a great read.
Now our comment block is out of date! Luckily, Rust supports one more feature that helps keep code comments from going stale: Documentation tests. For context, doc comments are written in markdown. By default, markdown code blocks in documentation comments are executed as code. That’s right, in Rust even your code comments are Turing-complete.
If we rewrite our comment just so, we can get cargo test
to run that assertion instead of relegating it to the dust bins of version control. Look! VS Code even uses appropriate syntax highlighting in the doc test block:
And the test will run with the rest of the unit tests when you run cargo test
, but for our screenshot’s sake, let’s just specify the documentation tests for now with cargo test --doc
and silence the backtrace with RUST_BACKTRACE=0
. So the full command is RUST_BACKTRACE=0 cargo test --doc
:
And if we fix the documentation…
…we fix the tests:
And look at that slick documentation!
I loooove Rust’s documentation features. It’s one of those tools that work so well that I end up wanting to document my code if nothing else just to admire the documentation later. I loved it so much I set up a GitHub Actions workflow at work to publish our documentation to our repo’s GitHub Pages so all my coworkers could admire their handiwork as well.
Bonus: GitHub Action
More on that last part. I’m not going to go into too much detail here, but on your repo you can set the Page to deploy from a GitHub Actions workflow:
Then you can use the workflow below to deploy your documentation on every merge to a branch of your choice ("develop"
in this case):
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static docs content to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ["develop"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Pages
uses: actions/configure-pages@v3
- uses: actions-rs/cargo@v1
with:
command: doc
- name: index.html redirect
uses: "DamianReeves/write-file-action@master"
with:
path: ./target/doc/index.html
write-mode: overwrite
contents: |
<meta http-equiv="Refresh" content="0; url='🚧YOUR_CRATE_NAME🚧/index.html'" />
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
# Upload docs repository
path: './target/doc'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
Because the documentation is stored in YOUR_CRATE/target/doc
and there’s no top-level index.html
, we need to create one that simply redirects to your crate’s documentation. That’s what the DamianReeves/write-file-action@master
action is for.
GitHub should give you a short url at https://YOUR_ORG_NAME.github.io/YOUR_REPO_NAME
which will redirect you to some Heroku-style subdomain. You can even limit the visibility to contributors-only.
That’s it! Hope you enjoy Rust’s documentation features as much as I do.
[0]: Apparently this is all covered in the guide to “Publishing on crates.io“
[1]: There is also Javadoc, Pydoc, and YARD (Ruby) docs for generating documentation.
Leave a Reply