From 881820aa062cb75ee02ec2d2a89a5a89074c1bd0 Mon Sep 17 00:00:00 2001 From: Andrew Gauger Date: Fri, 21 Aug 2020 17:19:56 -0700 Subject: [PATCH] Added Rustdoc book sections for linting and embedding more examples Co-authored-by: Joshua Nelson --- src/doc/rustdoc/src/SUMMARY.md | 2 + .../rustdoc/src/how-to-write-documentation.md | 120 ++++++++++++++--- src/doc/rustdoc/src/references.md | 31 +++++ src/doc/rustdoc/src/what-is-rustdoc.md | 35 ++--- src/doc/rustdoc/src/what-to-include.md | 125 ++++++++++++++++++ src/librustdoc/lib.rs | 1 + 6 files changed, 283 insertions(+), 31 deletions(-) create mode 100644 src/doc/rustdoc/src/references.md create mode 100644 src/doc/rustdoc/src/what-to-include.md diff --git a/src/doc/rustdoc/src/SUMMARY.md b/src/doc/rustdoc/src/SUMMARY.md index a3fa525be1db0..943aa09f678d7 100644 --- a/src/doc/rustdoc/src/SUMMARY.md +++ b/src/doc/rustdoc/src/SUMMARY.md @@ -2,6 +2,7 @@ - [What is rustdoc?](what-is-rustdoc.md) - [How to write documentation](how-to-write-documentation.md) +- [What to include (and exclude)](what-to-include.md) - [Command-line arguments](command-line-arguments.md) - [The `#[doc]` attribute](the-doc-attribute.md) - [Documentation tests](documentation-tests.md) @@ -10,3 +11,4 @@ - [Passes](passes.md) - [Advanced features](advanced-features.md) - [Unstable features](unstable-features.md) +- [References](references.md) diff --git a/src/doc/rustdoc/src/how-to-write-documentation.md b/src/doc/rustdoc/src/how-to-write-documentation.md index dd3aa5d4b6988..ca6db26da3130 100644 --- a/src/doc/rustdoc/src/how-to-write-documentation.md +++ b/src/doc/rustdoc/src/how-to-write-documentation.md @@ -1,14 +1,85 @@ # How to write documentation +Good documentation is not natural. There are opposing goals that make writing +good documentation difficult. It requires expertise in the subject but also +writing to a novice perspective. Documentation therefore often glazes over +implementation detail, or leaves readers with unanswered questions. + +There are a few tenets to Rust documentation that can help guide anyone through +the process of documenting libraries so that everyone has an ample opportunity +to use the code. + This chapter covers not only how to write documentation but specifically -how to write **good** documentation. Something to keep in mind when -writing documentation is that your audience is not just yourself but others -who simply don't have the context you do. It is important to be as clear +how to write **good** documentation. It is important to be as clear as you can, and as complete as possible. As a rule of thumb: the more documentation you write for your crate the better. If an item is public then it should be documented. -## Basic structure +## Getting Started + +Documenting a crate should begin with front-page documentation. As an +example, the [`hashbrown`] crate level documentation summarizes the role of +the crate, provides links to explain technical details, and explains why you +would want to use the crate. + +After introducing the crate, it is important that the front-page gives +an example of how to use the crate in a real world setting. Stick to the +library's role in the example, but do so without shortcuts to benefit users who +may copy and paste the example to get started. + +[`futures`] uses inline comments to explain line by line +the complexities of using a [`Future`], because a person's first exposure to +rust's [`Future`] may be this example. + +The [`backtrace`] documentation walks through the whole process, explaining +changes made to the `Cargo.toml` file, passing command line arguments to the +compiler, and shows a quick example of backtrace in the wild. + +Finally, the front-page can eventually become a comprehensive reference +how to use a crate, like [`regex`]. In this front page, all +requirements are outlined, the edge cases shown, and practical examples +provided. The front page goes on to show how to use regular expressions +then concludes with crate features. + +Don't worry about comparing your crate, which is just beginning, to other more +developed crates. To get the documentation to something more polished, start +incrementally and put in an introduction, example, and features. Rome was not +built in a day! + +The first lines within the `lib.rs` will compose the front-page, and they +use a different convention than the rest of the rustdocs. Lines should +start with `//!` which indicate module-level or crate-level documentation. +Here's a quick example of the difference: + +```rust,ignore +//! Fast and easy queue abstraction. +//! +//! Provides an abstraction over a queue. When the abstraction is used +//! there are these advantages: +//! - Fast +//! - [`Easy`] +//! +//! [`Easy`]: http://thatwaseasy.example.com + +/// This module makes it easy. +pub mod easy { + + /// Use the abstract function to do this specific thing. + pub fn abstract() {} + +} +``` + +Ideally, this first line of documentation is a sentence without highly +technical details, but with a good description of where this crate fits +within the rust ecosystem. Users should know whether this crate meets their use +case after reading this line. + +## Documenting components + +Whether it is modules, structs, functions, or macros: the public +API of all code should have documentation. Rarely does anyone +complain about too much documentation! It is recommended that each item's documentation follows this basic structure: @@ -23,9 +94,9 @@ It is recommended that each item's documentation follows this basic structure: ``` This basic structure should be straightforward to follow when writing your -documentation and, while you might think that a code example is trivial, -the examples are really important because they can help your users to -understand what an item is, how it is used, and for what purpose it exists. +documentation; while you might think that a code example is trivial, +the examples are really important because they can help users understand +what an item is, how it is used, and for what purpose it exists. Let's see an example coming from the [standard library] by taking a look at the [`std::env::args()`][env::args] function: @@ -62,21 +133,40 @@ for argument in env::args() { [`args_os`]: ./fn.args_os.html `````` +Everything before the first empty line will be reused to describe the component +in searches and module overviews. For example, the function `std::env::args()` +above will be shown on the [`std::env`] module documentation. It is good +practice to keep the summary to one line: concise writing is a goal of good +documentation. + +Because the type system does a good job of defining what types a function +passes and returns, there is no benefit of explicitly writing it +into the documentation, especially since `rustdoc` adds hyper links to all types in the function signature. + +In the example above, a 'Panics' section explains when the code might abruptly exit, +which can help the reader prevent reaching a panic. A panic section is recommended +every time edge cases in your code can be reached if known. + As you can see, it follows the structure detailed above: it starts with a short sentence explaining what the functions does, then it provides more information and finally provides a code example. ## Markdown -`rustdoc` is using the [commonmark markdown specification]. You might be +`rustdoc` uses the [commonmark markdown specification]. You might be interested into taking a look at their website to see what's possible to do. + - [commonmark quick reference] + - [current spec] -## Lints - -To be sure that you didn't miss any item without documentation or code examples, -you can take a look at the rustdoc lints [here][rustdoc-lints]. -[standard library]: https://doc.rust-lang.org/stable/std/index.html -[env::args]: https://doc.rust-lang.org/stable/std/env/fn.args.html +[`backtrace`]: https://docs.rs/backtrace/0.3.50/backtrace/ [commonmark markdown specification]: https://commonmark.org/ -[rustdoc-lints]: lints.md +[commonmark quick reference]: https://commonmark.org/help/ +[env::args]: https://doc.rust-lang.org/stable/std/env/fn.args.html +[`Future`]: https://doc.rust-lang.org/std/future/trait.Future.html +[`futures`]: https://docs.rs/futures/0.3.5/futures/ +[`hashbrown`]: https://docs.rs/hashbrown/0.8.2/hashbrown/ +[`regex`]: https://docs.rs/regex/1.3.9/regex/ +[standard library]: https://doc.rust-lang.org/stable/std/index.html +[current spec]: https://spec.commonmark.org/current/ +[`std::env`]: https://doc.rust-lang.org/stable/std/env/index.html#functions diff --git a/src/doc/rustdoc/src/references.md b/src/doc/rustdoc/src/references.md new file mode 100644 index 0000000000000..1e050e321d2e3 --- /dev/null +++ b/src/doc/rustdoc/src/references.md @@ -0,0 +1,31 @@ +# References + +There are many great `rustdoc` references out there. +If you know of other great resources, please submit a pull request! + +## Official + +- [Learn Rust] +- [Rust By Example] +- [Rust Reference] +- [RFC 1574: More API Documentation Conventions] +- [RFC 1946: Intra Rustdoc Links] + +## Community +- [API Guidelines] +- [Github tagged RFCs] +- [Github tagged issues] +- [RFC (stalled) front page styleguide] +- [Guide on how to write documenation for a Rust crate] + + +[API Guidelines]: https://rust-lang.github.io/api-guidelines/documentation.html +[Github tagged RFCs]: https://github.com/rust-lang/rfcs/issues?q=label%3AT-rustdoc +[Github tagged issues]: https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+label%3AT-rustdoc +[Guide on how to write documenation for a Rust crate]: https://blog.guillaume-gomez.fr/articles/2020-03-12+Guide+on+how+to+write+documentation+for+a+Rust+crate +[Learn Rust]: https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#making-useful-documentation-comments +[RFC 1574: More API Documentation Conventions]: https://rust-lang.github.io/rfcs/1574-more-api-documentation-conventions.html +[RFC 1946: Intra Rustdoc Links]: https://rust-lang.github.io/rfcs/1946-intra-rustdoc-links.html +[RFC (stalled) front page styleguide]: https://github.com/rust-lang/rfcs/pull/1687 +[Rust By Example]: https://doc.rust-lang.org/stable/rust-by-example/meta/doc.html +[Rust Reference]: https://doc.rust-lang.org/stable/reference/comments.html#doc-comments \ No newline at end of file diff --git a/src/doc/rustdoc/src/what-is-rustdoc.md b/src/doc/rustdoc/src/what-is-rustdoc.md index 7a38c96d7147b..1f6dced180b96 100644 --- a/src/doc/rustdoc/src/what-is-rustdoc.md +++ b/src/doc/rustdoc/src/what-is-rustdoc.md @@ -7,14 +7,14 @@ CSS, and JavaScript. ## Basic usage -Let's give it a try! Let's create a new project with Cargo: +Let's give it a try! Create a new project with Cargo: ```bash $ cargo new docs $ cd docs ``` -In `src/lib.rs`, you'll find that Cargo has generated some sample code. Delete +In `src/lib.rs`, Cargo has generated some sample code. Delete it and replace it with this: ```rust @@ -31,8 +31,12 @@ $ rustdoc src/lib.rs This will create a new directory, `doc`, with a website inside! In our case, the main page is located in `doc/lib/index.html`. If you open that up in -a web browser, you'll see a page with a search bar, and "Crate lib" at the -top, with no contents. There's two problems with this: first, why does it +a web browser, you will see a page with a search bar, and "Crate lib" at the +top, with no contents. + +## Configuring rustdoc + +There are two problems with this: first, why does it think that our package is named "lib"? Second, why does it not have any contents? @@ -46,7 +50,7 @@ $ rustdoc src/lib.rs --crate-name docs Now, `doc/docs/index.html` will be generated, and the page says "Crate docs." -For the second issue, it's because our function `foo` is not public; `rustdoc` +For the second issue, it is because our function `foo` is not public; `rustdoc` defaults to generating documentation for only public functions. If we change our code... @@ -61,7 +65,7 @@ pub fn foo() {} $ rustdoc src/lib.rs --crate-name docs ``` -We'll have some generated documentation. Open up `doc/docs/index.html` and +We now have some generated documentation. Open up `doc/docs/index.html` and check it out! It should show a link to the `foo` function's page, which is located at `doc/docs/fn.foo.html`. On that page, you'll see the "foo is a function" we put inside the documentation comment in our crate. @@ -85,13 +89,12 @@ dependency=/docs/target/debug/deps You can see this with `cargo doc --verbose`. It generates the correct `--crate-name` for us, as well as pointing to -`src/lib.rs` But what about those other arguments? `-o` controls the -*o*utput of our docs. Instead of a top-level `doc` directory, you'll -notice that Cargo puts generated documentation under `target`. That's -the idiomatic place for generated files in Cargo projects. Also, it -passes `-L`, a flag that helps rustdoc find the dependencies -your code relies on. If our project used dependencies, we'd get -documentation for them as well! +`src/lib.rs`. But what about those other arguments? + - `-o` controls the *o*utput of our docs. Instead of a top-level + `doc` directory, notice that Cargo puts generated documentation under + `target`. That is the idiomatic place for generated files in Cargo projects. + - `-L` flag helps rustdoc find the dependencies your code relies on. + If our project used dependencies, we would get documentation for them as well! ## Outer and inner documentation @@ -118,7 +121,7 @@ For more information about the `//!` syntax, see [the Book]. ## Using standalone Markdown files -`rustdoc` can also generate HTML from standalone Markdown files. Let's +`rustdoc` can also generate HTML from standalone Markdown files. Let' s give it a try: create a `README.md` file with these contents: ````text @@ -128,7 +131,7 @@ This is a project to test out `rustdoc`. [Here is a link!](https://www.rust-lang.org) -## Subheading +## Example ```rust fn foo() -> i32 { @@ -143,7 +146,7 @@ And call `rustdoc` on it: $ rustdoc README.md ``` -You'll find an HTML file in `docs/doc/README.html` generated from its +You will find an HTML file in `docs/doc/README.html` generated from its Markdown contents. Cargo currently does not understand standalone Markdown files, unfortunately. diff --git a/src/doc/rustdoc/src/what-to-include.md b/src/doc/rustdoc/src/what-to-include.md new file mode 100644 index 0000000000000..878c75baae727 --- /dev/null +++ b/src/doc/rustdoc/src/what-to-include.md @@ -0,0 +1,125 @@ +# What to include (and exclude) + +It is easy to say everything must be documented in a project and often times +that is correct, but how can we get there, and are there things that don't +belong? + +At the top of the `src/lib.rs` or `main.rs` file in your binary project, include +the following attribute: + +```rust +#![warn(missing_docs)] +``` + +Now run `cargo doc` and examine the output. Here's a sample: + +```text + Documenting docdemo v0.1.0 (/Users/username/docdemo) +warning: missing documentation for the crate + --> src/main.rs:1:1 + | +1 | / #![warn(missing_docs)] +2 | | +3 | | fn main() { +4 | | println!("Hello, world!"); +5 | | } + | |_^ + | +note: the lint level is defined here + --> src/main.rs:1:9 + | +1 | #![warn(missing_docs)] + | ^^^^^^^^^^^^ + +warning: 1 warning emitted + + Finished dev [unoptimized + debuginfo] target(s) in 2.96s +``` + +As a library author, adding the lint `#![deny(missing_docs)]` is a great way to +ensure the project does not drift away from being documented well, and +`#![warn(missing_docs)]` is a good way to move towards comprehensive +documentation. In addition to docs, `#![deny(missing_doc_code_examples)]` +ensures each function contains a usage example. In our example above, the +warning is resolved by adding crate level documentation. + +There are more lints in the upcoming chapter [Lints][rustdoc-lints]. + +## Examples + +Of course this is contrived to be simple, but part of the power of documentation +is showing code that is easy to follow, rather than being realistic. Docs often +take shortcuts with error handling because examples can become complicated to +follow with all the necessary set up required for a simple example. + +`Async` is a good example of this. In order to execute an `async` example, an +executor needs to be available. Examples will often shortcut this, and leave +users to figure out how to put the `async` code into their own runtime. + +It is preferred that `unwrap()` not be used inside an example, and some of the +error handling components be hidden if they make the example too difficult to +follow. + +``````text +/// Example +/// ```rust +/// let fourtytwo = "42".parse::()?; +/// println!("{} + 10 = {}", fourtytwo, fourtytwo+10); +/// ``` +`````` + +When rustdoc wraps that in a main function, it will fail to compile because the +`ParseIntError` trait is not implemented. In order to help both your audience +and your test suite, this example needs some additional code: + +``````text +/// Example +/// ```rust +/// # main() -> Result<(), std::num::ParseIntError> { +/// let fortytwo = "42".parse::()?; +/// println!("{} + 10 = {}", fortytwo, fortytwo+10); +/// # Ok(()) +/// # } +/// ``` +`````` + +The example is the same on the doc page, but has that extra information +available to anyone trying to use your crate. More about tests in the +upcoming [Documentation tests] chapter. + +## What to Exclude + +Certain parts of your public interface may be included by default in the output +of rustdoc. The attribute `#[doc(hidden)]` can hide implementation details +to encourage idiomatic use of the crate. + +For example, an internal `macro!` that makes the crate easier to implement can +become a footgun for users when it appears in the public documentation. An +internal `Error` type may exist, and `impl` details should be hidden, as +detailed in the [API Guidelines]. + +## Customizing the output + +It is possible to pass a custom css file to `rustdoc` and style the +documentation. + +```bash +rustdoc --extend-css custom.css src/lib.rs +``` + +A good example of using this feature to create a dark theme is documented [on +this blog]. Just remember, dark theme is already included in the rustdoc output +by clicking on the paintbrush. Adding additional options to the themes are +as easy as creating a custom theme `.css` file and using the following syntax: + +```bash +rustdoc --theme awesome.css src/lib.rs +``` + +Here is an example of a new theme, [Ayu]. + +[Ayu]: https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/static/themes/ayu.css +[API Guidelines]: https://rust-lang.github.io/api-guidelines/documentation.html#rustdoc-does-not-show-unhelpful-implementation-details-c-hidden +[Documentation tests]: documentation-tests.md +[on this blog]: https://blog.guillaume-gomez.fr/articles/2016-09-16+Generating+doc+with+rustdoc+and+a+custom+theme +[rustdoc-lints]: lints.md \ No newline at end of file diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 12726d2bd9a3e..752b24bc92911 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -418,6 +418,7 @@ fn usage(argv0: &str) { (option.apply)(&mut options); } println!("{}", options.usage(&format!("{} [options] ", argv0))); + println!("More information available at https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html") } /// A result type used by several functions under `main()`.