How to rustdoc

For 0.2 Rust gained a new API documentation tool, rustdoc for creating Rust API documentation (also called ‘rustdocs’). It is in its infancy but is already being used for the core/std documentation hosted on rust-lang.org.

What a rustdoc is

A rustdoc is just a snippet of markdown that is associated in some way with a unit of Rust code. There is very little structure imposed on the documentation itself. Unlike javadoc and other tools there is no internal markup for identifying things like arguments. How the documentation is written is a matter of convention.

Rustdocs can be as simple as Returns a slice or they can be more involved:

Adopt a slice of a string as a node.

If the slice is longer than `max_leaf_char_len`, it is logically split
between as many leaves as necessary. Regardless, the string itself
is not copied

# Arguments

* `byte_start` - The byte offset where the slice of `str` starts.
* `byte_len`   - The number of bytes from `str` to use.

# Safety note

Behavior is undefined if `byte_start` or `byte_len` do not represent
valid positions in `str`

# Example

    let slice = of_substr(s, 0u, 1u);

Where the rustdocs live

Rustdocs must be put inside doc attributes, which are name-value attributes called “doc” with a string value.

#[doc = "Engage thrusters"]
fn thrusters_on() { ... }

To write a more involved rustdoc you just use a multiline string:

#[doc = "
Engage thrusters

Begin heating toast at the temperature specified by the temperature dial
"]
fn thrusters_on() { ... }

Because rustdocs can go anywhere attributes can go it is also possible to write function documentation inside the function definition by terminating the doc attribute with a semicolon.

fn thrusters_on() {
    #[doc = "Engage thrusters"];
    ...
}

Putting rustdocs inside strings works fairly well most of the time, but does result in some quirks. In particular you must remember to escape double quotes. Because of this limitation I usually end up using single quotes for quoted text. For writing code examples inside of rustdocs there’s no way to avoid double-quotes though. Here’s a particularly heinous example from core:

Splits a string into a vector of the substrings separated by a given string

# Example

~~~
assert [\"\", \"XXX\", \"YYY\", \"\"] == split_str(\".XXX.YYY.\", \".\")
~~~

The rustdoc tool understands what to do with doc attributes on crates, items, enum variants, iface methods and impl methods. doc attributes found in other places are silently ignored.

In future versions there will likely be other places to put rustdocs, like inside comments or here docs.

The rules of rustdoc

Though rustdoc imposes very few rules on what goes into a rustdoc, it does have two rules for processing documentation that are important to know.

The first is the summary rule, under which rustdoc will attempt to use the first sentence of the rustdoc as a “summary” of the documentation. This summary is then used in the documentation index. Rustdocs are often formulated like

Get the next victim

Get the next victim from the holding chambers. If there are no futher
victims in the holding chambers then return `none`.

The documentation states in a few words what a function does, then reiterates and expands on that. This is because the first line is “the summary”. Another common variant is to just make the first sentence short and simple:

Get the next victim. If there are no further victims in the holding
chambers then return `none`.

In this case rustdoc will use the text up to the first period as the summary. There is an arbitrary limit on the length of a summary so make them brief. If the first sentence is too long then rustdoc will not create a summary.

The second rule is the header rule.

Headers in rustdoc should be indicated with a single “#”. Though markdown supports multiple ways to write headers and multiple header levels, rustdoc uses headers to interpret the structure of docs and currently the only header markup that rustdoc understands is “#”. This should be eased in the future.

# This is a valid rustdoc header

#### Please don't do this

Probably should avoid this too
==============================

Even though rustdocs are just markdown, we still need to agree on some conventions. Here is the official style used by the core and standard libraries.

The first sentence should describe, succinctly, what the item does. When that is not sufficient to describe the behavior of the item then prose should be used to fill out the description. It should discuss both what and why the item does what it does, relevant inputs and outputs, and should always mention any conditions under which the code might fail.

At some level of documentation complexity, simply writing prose about all the possible inputs, outputs, effects, etc. gets unwieldy, and the document should be broken into headered sections. Standard headers are “Arguments”, “Return value”, “Failure”, “Example”, “Safety notes”, and “Performance notes”.

The argument section should be written as a bulleted list with the following form

# Arguments

* `s` - The input string
* `n` - The starting index

Operating the rustdoc tool

The simplest way to use rustdoc is by just passing it the crate file to document, rustdoc core.rc. This will output html documentation in the current directory.

The generated HTML expects to find a stylesheet called rust.css but does not provide it. The one used for all of rust’s documentation, including the tutorial and manual, can be found in the rust repo under “src/doc”. Eventually rustdoc will provide the sheet.

Rustdoc has a few other options. --output-format allows you to output plain markdown, and --output-style can be used to generate all the documentation on a single page (by default each mod gets its own page).

Usage: rustdoc [options] <cratefile>

Options:

    --output-dir <val>     put documents here
    --output-format <val>  either 'markdown' or 'html'
    --output-style <val>   either 'doc-per-crate' or 'doc-per-mod'
    --pandoc-cmd <val>     the command for running pandoc
    -h                     print help

Other tidbits and limitations

Sometimes you don’t want to advertise your public API. In that case you can use the #[doc(hidden)] attribute and rustdoc will pretend the thing doesn’t exist.

There are many flavors of markdown, and rustdoc uses Pandoc. As such rustdoc is subject to any Pandoc quirks or extensions. In general I recommend sticking to pure markdown in most cases, with the exception of code blocks. For code blocks using tilde-fences is the most reliable way of getting the correct output. Using indented blocks works but is prone to ambiguities. Backtick fences, like on github, do not work.

# This works

~~~
code here
~~~

# This usually works

    code here

# This does not work

````
code here
````

Future work

There are still a lot of limitations and bugs. Some of the upcoming features:

  • Support for new language features like classes
  • Understand conditional compilation so that docs can say something useful about, for example, FreeBSD-specific functions even though the docs were built on Linux
  • More internal links
  • Best effort links to backquoted things, e.g. vec links to the vec module
  • Beautification
  • Extract docs directly from compiled crates
  • rustdoc as a local documentation search engine

If anybody is interested in improving the aesthetics of the generated rustdoc documentation your contribution will be welcomed with much enthusiasm.