Skip to content

Commit 59f6b83

Browse files
committed
Auto merge of #43812 - steveklabnik:rustdoc-doc-tests, r=QuietMisdreavus
rustdoc doc tests This is mostly adapted from the book v1's chapter. cc #42322 r? @rust-lang/docs
2 parents bffc973 + 035d4ea commit 59f6b83

File tree

1 file changed

+237
-1
lines changed

1 file changed

+237
-1
lines changed
+237-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,239 @@
11
# Documentation tests
22

3-
Coming soon!
3+
`rustdoc` supports executing your documentation examples as tests. This makes sure
4+
that your tests are up to date and working.
5+
6+
The basic idea is this:
7+
8+
```rust,ignore
9+
/// # Examples
10+
///
11+
/// ```
12+
/// let x = 5;
13+
/// ```
14+
```
15+
16+
The triple backticks start and end code blocks. If this were in a file named `foo.rs`,
17+
running `rustdoc --test foo.rs` will extract this example, and then run it as a test.
18+
19+
There's some subtlety though! Read on for more details.
20+
21+
## Pre-processing examples
22+
23+
In the example above, you'll note something strange: there's no `main`
24+
function! Forcing you to write `main` for every example, no matter how small,
25+
adds friction. So `rustdoc` processes your examples slightly before
26+
running them. Here's the full algorithm rustdoc uses to preprocess examples:
27+
28+
1. Any leading `#![foo]` attributes are left intact as crate attributes.
29+
2. Some common `allow` attributes are inserted, including
30+
`unused_variables`, `unused_assignments`, `unused_mut`,
31+
`unused_attributes`, and `dead_code`. Small examples often trigger
32+
these lints.
33+
3. If the example does not contain `extern crate`, then `extern crate
34+
<mycrate>;` is inserted (note the lack of `#[macro_use]`).
35+
4. Finally, if the example does not contain `fn main`, the remainder of the
36+
text is wrapped in `fn main() { your_code }`.
37+
38+
For more about that caveat in rule 3, see "Documeting Macros" below.
39+
40+
## Hiding portions of the example
41+
42+
Sometimes, you need some setup code, or other things that would distract
43+
from your example, but are important to make the tests work. Consider
44+
an example block that looks like this:
45+
46+
```text
47+
/// Some documentation.
48+
# fn foo() {}
49+
```
50+
51+
It will render like this:
52+
53+
```rust
54+
/// Some documentation.
55+
# fn foo() {}
56+
```
57+
58+
Yes, that's right: you can add lines that start with `# `, and they will
59+
be hidden from the output, but will be used when compiling your code. You
60+
can use this to your advantage. In this case, documentation comments need
61+
to apply to some kind of function, so if I want to show you just a
62+
documentation comment, I need to add a little function definition below
63+
it. At the same time, it's only there to satisfy the compiler, so hiding
64+
it makes the example more clear. You can use this technique to explain
65+
longer examples in detail, while still preserving the testability of your
66+
documentation.
67+
68+
For example, imagine that we wanted to document this code:
69+
70+
```rust
71+
let x = 5;
72+
let y = 6;
73+
println!("{}", x + y);
74+
```
75+
76+
We might want the documentation to end up looking like this:
77+
78+
> First, we set `x` to five:
79+
>
80+
> ```rust
81+
> let x = 5;
82+
> # let y = 6;
83+
> # println!("{}", x + y);
84+
> ```
85+
>
86+
> Next, we set `y` to six:
87+
>
88+
> ```rust
89+
> # let x = 5;
90+
> let y = 6;
91+
> # println!("{}", x + y);
92+
> ```
93+
>
94+
> Finally, we print the sum of `x` and `y`:
95+
>
96+
> ```rust
97+
> # let x = 5;
98+
> # let y = 6;
99+
> println!("{}", x + y);
100+
> ```
101+
102+
To keep each code block testable, we want the whole program in each block, but
103+
we don't want the reader to see every line every time. Here's what we put in
104+
our source code:
105+
106+
```text
107+
First, we set `x` to five:
108+
109+
```rust
110+
let x = 5;
111+
# let y = 6;
112+
# println!("{}", x + y);
113+
```
114+
115+
Next, we set `y` to six:
116+
117+
```rust
118+
# let x = 5;
119+
let y = 6;
120+
# println!("{}", x + y);
121+
```
122+
123+
Finally, we print the sum of `x` and `y`:
124+
125+
```rust
126+
# let x = 5;
127+
# let y = 6;
128+
println!("{}", x + y);
129+
```
130+
```
131+
132+
By repeating all parts of the example, you can ensure that your example still
133+
compiles, while only showing the parts that are relevant to that part of your
134+
explanation.
135+
136+
Another case where the use of `#` is handy is when you want to ignore
137+
error handling. Lets say you want the following,
138+
139+
```rust,ignore
140+
/// use std::io;
141+
/// let mut input = String::new();
142+
/// io::stdin().read_line(&mut input)?;
143+
```
144+
145+
The problem is that `?` returns a `Result<T, E>` and test functions
146+
don't return anything so this will give a mismatched types error.
147+
148+
```rust,ignore
149+
/// A doc test using ?
150+
///
151+
/// ```
152+
/// use std::io;
153+
/// # fn foo() -> io::Result<()> {
154+
/// let mut input = String::new();
155+
/// io::stdin().read_line(&mut input)?;
156+
/// # Ok(())
157+
/// # }
158+
/// ```
159+
# fn foo() {}
160+
```
161+
162+
You can get around this by wrapping the code in a function. This catches
163+
and swallows the `Result<T, E>` when running tests on the docs. This
164+
pattern appears regularly in the standard library.
165+
166+
### Documenting macros
167+
168+
Here’s an example of documenting a macro:
169+
170+
```rust
171+
/// Panic with a given message unless an expression evaluates to true.
172+
///
173+
/// # Examples
174+
///
175+
/// ```
176+
/// # #[macro_use] extern crate foo;
177+
/// # fn main() {
178+
/// panic_unless!(1 + 1 == 2, “Math is broken.”);
179+
/// # }
180+
/// ```
181+
///
182+
/// ```rust,should_panic
183+
/// # #[macro_use] extern crate foo;
184+
/// # fn main() {
185+
/// panic_unless!(true == false, “I’m broken.”);
186+
/// # }
187+
/// ```
188+
#[macro_export]
189+
macro_rules! panic_unless {
190+
($condition:expr, $($rest:expr),+) => ({ if ! $condition { panic!($($rest),+); } });
191+
}
192+
# fn main() {}
193+
```
194+
195+
You’ll note three things: we need to add our own `extern crate` line, so that
196+
we can add the `#[macro_use]` attribute. Second, we’ll need to add our own
197+
`main()` as well (for reasons discussed above). Finally, a judicious use of
198+
`#` to comment out those two things, so they don’t show up in the output.
199+
200+
## Attributes
201+
202+
There are a few annotations that are useful to help `rustdoc` do the right
203+
thing when testing your code:
204+
205+
```rust
206+
/// ```ignore
207+
/// fn foo() {
208+
/// ```
209+
# fn foo() {}
210+
```
211+
212+
The `ignore` directive tells Rust to ignore your code. This is almost never
213+
what you want, as it's the most generic. Instead, consider annotating it
214+
with `text` if it's not code, or using `#`s to get a working example that
215+
only shows the part you care about.
216+
217+
```rust
218+
/// ```should_panic
219+
/// assert!(false);
220+
/// ```
221+
# fn foo() {}
222+
```
223+
224+
`should_panic` tells `rustdoc` that the code should compile correctly, but
225+
not actually pass as a test.
226+
227+
```rust
228+
/// ```no_run
229+
/// loop {
230+
/// println!("Hello, world");
231+
/// }
232+
/// ```
233+
# fn foo() {}
234+
```
235+
236+
The `no_run` attribute will compile your code, but not run it. This is
237+
important for examples such as "Here's how to retrieve a web page,"
238+
which you would want to ensure compiles, but might be run in a test
239+
environment that has no network access.

0 commit comments

Comments
 (0)