diff --git a/practices/doc-comments/.gitignore b/practices/doc-comments/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/practices/doc-comments/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/practices/doc-comments/Cargo.lock b/practices/doc-comments/Cargo.lock new file mode 100644 index 0000000..9477981 --- /dev/null +++ b/practices/doc-comments/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "doc-comments" +version = "0.1.0" diff --git a/practices/doc-comments/Cargo.toml b/practices/doc-comments/Cargo.toml new file mode 100644 index 0000000..335de20 --- /dev/null +++ b/practices/doc-comments/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "doc-comments" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/practices/doc-comments/src/compute.rs b/practices/doc-comments/src/compute.rs new file mode 100644 index 0000000..afc0842 --- /dev/null +++ b/practices/doc-comments/src/compute.rs @@ -0,0 +1,36 @@ +//! Do some complicated arithmetic that you can't do by yourself + +/// # Panics +/// +/// The function panics if the second argument is zero. +/// +/// ```rust,should_panic +/// // panics on division by zero +/// doc_comments::compute::div(10, 0); +/// ``` +pub fn div(a: i32, b: i32) -> i32 { + if b == 0 { + panic!("Divide-by-zero error"); + } + + a / b +} + + +/// ``` +/// # fn try_main() -> Result<(), String> { +/// let res = doc_comments::compute::try_div(10, 1)?; +/// # Ok(()) // returning from try_main +/// # } +/// # fn main() { +/// # try_main().unwrap(); +/// # +/// # } +/// ``` +pub fn try_div(a: i32, b: i32) -> Result { + if b == 0 { + Err(String::from("Divide-by-zero")) + } else { + Ok(a / b) + } +} \ No newline at end of file diff --git a/practices/doc-comments/src/lib.rs b/practices/doc-comments/src/lib.rs new file mode 100644 index 0000000..645b77f --- /dev/null +++ b/practices/doc-comments/src/lib.rs @@ -0,0 +1,54 @@ +//! # Doc comments +//! +//! A library for showing how to use doc comments + +pub mod compute; + +/// Add one to the given value and return a new value +/// +/// # Examples +/// +/// ``` +/// let arg = 5; +/// let answer = doc_comments::add_one(arg); +/// +/// assert_eq!(6, answer); +/// ``` +pub fn add_one(x: i32) -> i32 { + x + 1 +} + + + +/** Add two to the given value and return a new value + +# Examples + +``` +let arg = 5; +let answer = doc_comments::add_two(arg); + +assert_eq!(7, answer); +``` +*/ +pub fn add_two(x: i32) -> i32 { + x + 2 +} + + +/// Add three to the given value and return a [`Option`] type +pub fn add_three(x: i32) -> Option { + Some(x + 3) +} + +mod a { + /// Add four to the given value and return a [`Option`] type + /// [`crate::MySpecialFormatter`] + pub fn add_four(x: i32) -> Option { + Some(x + 4) + } +} + +struct MySpecialFormatter; + + diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 0133fa2..1ac15c1 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -43,8 +43,8 @@ - [Package and Crate](crate-module/crate.md) - [Module](crate-module/module.md) - [Advanced use and pub](crate-module/use-pub.md) -- [Comments and Docs TODO](comments-docs.md) -- [Formatted output TODO](formatted-output.md) +- [Comments and Docs](comments-docs.md) +- [Formatted output](formatted-output.md) - [Lifetime TODO](lifetime/intro.md) - [basic](lifetime/basic.md) - [&'static and T: 'static](lifetime/static.md) diff --git a/src/comments-docs.md b/src/comments-docs.md index cb7d70d..3c0894d 100644 --- a/src/comments-docs.md +++ b/src/comments-docs.md @@ -1 +1,295 @@ # Comments and Docs +Every program requires comments: + + +## Comments +- Regular comments which are ignored by the compiler: + - `// Line comment, which goes to the end of the line` + - `/* Block comment, which goes to the end of the closing delimiter */` + + +### Examples +```rust +fn main() { + // This is an example of a line comment + // There are two slashes at the beginning of the line + // And nothing written inside these will be read by the compiler + + // println!("Hello, world!"); + + // Run it. See? Now try deleting the two slashes, and run it again. + + /* + * This is another type of comment, a block comment. In general, + * line comments are the recommended comment style. But + * block comments are extremely useful for temporarily disabling + * chunks of code. /* Block comments can be /* nested, */ */ + * so it takes only a few keystrokes to comment out everything + * in this main() function. /*/*/* Try it yourself! */*/*/ + */ + + /* + Note: The previous column of `*` was entirely for style. There's + no actual need for it. + */ +} +``` + +### Exercises +1. 🌟🌟 +```rust,editable + +/* Make it work, only using comments! */ +fn main() { + todo!(); + unimplemented!(); + + assert_eq!(6, 5 + 3 + 2 + 1 ) +} +``` + + +## Doc Comments +- Doc comments which are parsed into HTML and supported `Markdown` + - `/// Generate library docs for the following item` + - `//! Generate library docs for the eclosing item` + +Before starting, we need to create a new package for practice: `cargo new --lib doc-comments`. + + +### Line doc comments `///` +Add docs for function `add_one` +```rust +// in lib.rs + +/// Add one to the given value and return the value +/// +/// # Examples +/// +/// ``` +/// let arg = 5; +/// let answer = my_crate::add_one(arg); +/// +/// assert_eq!(6, answer); +/// ``` +pub fn add_one(x: i32) -> i32 { + x + 1 +} +``` + +### Cargo doc +We can use `cargo doc --open` to generate html files and open them in the browser. + +### Block doc comments `/** ... */` +Add docs for function `add_two`: +```rust +/** Add two to the given value and return a new value + +# Examples + +let arg = 5; +let answer = my_crate::add_two(arg); + +assert_eq!(7, answer); + +*/ +pub fn add_two(x: i32) -> i32 { + x + 2 +} +``` + +### Doc comments for crate and module +We can also add doc comments for our crates and modules. + +Firstly, let's add some doc comments for our library crate: + +> Note: We mush place crates and module comments at the top of the crate root or module file. +```rust +//! # Doc comments +//! +//! A library for showing how to use doc comments + +// in lib.rs +pub mod compute; +``` + +You can also use block comments to achieve this: +```rust +/*! # Doc comments + + A library for showing how to use doc comments */ +``` + +Next, create a new module file `src/compute.rs`, and add following comments to it: +```rust +//! //! Do some complicated arithmetic that you can't do by yourself + +// in compute.rs +``` + +Then run `cargo doc --open` and see the results. + + +### Doc tests +The doc comments of `add_one` and `add_tow` contain two example code blocks. + +The examples can not only demonstrate how to use your library, but also running as test with `cargo test` command. + +2. 🌟🌟 But there are errors in the two examples, please fix them, and running with `cargo test` to get following result: +```shell +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests doc-comments + +running 2 tests +test src/lib.rs - add_one (line 11) ... ok +test src/lib.rs - add_two (line 26) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.55s +``` + +3. 🌟🌟 Sometimes we expect an example to be panic, add following code to `src/compute.rs` and make the `cargo test` passed. + +> You can only modify the comments, DON'T modify `fn div` + +```rust +// in src/compute.rs + +/// # Panics +/// +/// The function panics if the second argument is zero. +/// +/// ```rust,should_panic +/// // panics on division by zero +/// doc_comments::compute::div(10, 0); +/// ``` +pub fn div(a: i32, b: i32) -> i32 { + if b == 0 { + panic!("Divide-by-zero error"); + } + + a / b +} +``` + +4. 🌟🌟 Sometimes we want to hide the doc comments, but keep the doc tests. + +Add following code to `src/compute.rs` , + +```rust +// in src/compute.rs + +/// ``` +/// # fn try_main() -> Result<(), String> { +/// let res = doc_comments::compute::try_div(10, 0)?; +/// # Ok(()) // returning from try_main +/// # } +/// # fn main() { +/// # try_main().unwrap(); +/// # +/// # } +/// ``` +pub fn try_div(a: i32, b: i32) -> Result { + if b == 0 { + Err(String::from("Divide-by-zero")) + } else { + Ok(a / b) + } +} +``` + +and modify this code to achieve two goals: + +- The doc comments must not be presented in html files generated by `cargo doc --open` +- run the tests, you should see results as below: + +```shell +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + + Doc-tests doc-comments + +running 4 tests +test src/compute.rs - compute::div (line 7) ... ok +test src/lib.rs - add_two (line 27) ... ok +test src/lib.rs - add_one (line 11) ... ok +test src/compute.rs - compute::try_div (line 20) ... ok + +test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.51s +``` + +### Code navigation +Rust provide a very powerful feature for us, that is code navigation in doc comments. + +Add following code to `src/lib.rs`: +```rust +// in lib.rs + +/// Add one to the given value and return a [`Option`] type +pub fn add_three(x: i32) -> Option { + Some(x + 3) +} +``` + +Besides jump into the standard library, you can also jump to another module in the package. + +```rust +// in lib.rs + +mod a { + /// Add four to the given value and return a [`Option`] type + /// [`crate::MySpecialFormatter`] + pub fn add_four(x: i32) -> Option { + Some(x + 4) + } +} + +struct MySpecialFormatter; +``` + +### Doc attributes +Below are a few examples of the most common `#[doc]` attributes used with `rustdoc`. + +### `inline` + +Used to inline docs, instead of linking out to separate page. + +```rust,ignore +#[doc(inline)] +pub use bar::Bar; + +/// bar docs +mod bar { + /// the docs for Bar + pub struct Bar; +} +``` + +### `no_inline` + +Used to prevent linking out to separate page or anywhere. + +```rust,ignore +// Example from libcore/prelude +#[doc(no_inline)] +pub use crate::mem::drop; +``` + +### `hidden` + +Using this tells `rustdoc` not to include this in documentation: + +```rust,editable,ignore +// Example from the futures-rs library +#[doc(hidden)] +pub use self::async_await::*; +``` + +For documentation, `rustdoc` is widely used by the community. It's what is used to generate the [std library docs](https://doc.rust-lang.org/std/). + + +### Full Code +The full code of package `doc-comments` is [here](https://github.com/sunface/rust-by-practice/tree/master/practices/doc-comments).