注释和文档
此笔记记录于Rust Course,大多数为其中的摘要,少数为笔者自己的理解
注释的种类
在 Rust 中,注释分为三类:
- 代码注释,用于说明某一块代码的功能,读者往往是同一个项目的协作开发者
- 文档注释,支持
Markdown
,对项目描述、公共 API 等用户关心的功能进行介绍,同时还能提供示例代码,目标读者往往是想要了解你项目的人 - 包和模块注释,严格来说这也是文档注释中的一种,它主要用于说明当前包和模块的功能,方便用户迅速了解一个项目
通过这些注释,实现了 Rust 极其优秀的文档化支持,甚至你还能在文档注释中写测试用例,省去了单独写测试用例的环节,我直呼好家伙!
代码注释
文档注释
- 文档行注释
///
- 文档块注释
/** ... */
- 查看文档
cargo doc
常用文档标题:
- Panics:函数可能会出现的异常状况,这样调用函数的人就可以提前规避
- Errors:描述可能出现的错误及什么情况会导致错误,有助于调用者针对不同的错误采取不同的处理方式
- Safety:如果函数使用
unsafe
代码,那么调用者就需要注意一些使用条件,以确保unsafe
代码块的正常工作
包和模块级别的注释
这些注释要添加到包、模块的最上方!
包级别的注释也分为两种:行注释 //!
和块注释 /*! ... */
。
rust
/*! lib 包是 world_hello 二进制包的依赖包,
里面包含了 compute 等有用模块 */
pub mod compute;
文档测试
基本使用
rust
/// `add_one` 将指定值加 1
///
/// # Examples11
///
/// ```
/// let arg = 5;
/// let answer = world_hello::compute::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
x + 1
}
以上的注释不仅仅是文档,还可以作为单元测试的用例运行,使用 cargo test
运行测试:
Doc-tests world_hello
running 2 tests
test src/compute.rs - compute::add_one (line 8) ... ok
test src/compute.rs - compute::add_two (line 22) ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.00s
需要注意的是,你可能需要使用类如
world_hello::compute::add_one(arg)
的完整路径来调用函数,因为测试是在另外一个独立的线程中运行的
造成 panic 的文档测试
rust
/// # Panics
///
/// The function panics if the second argument is zero.
///
/// ```rust
/// // panics on division by zero
/// world_hello::compute::div(10, 0);
/// ```
pub fn div(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("Divide-by-zero error");
}
a / b
}
以上测试运行后会 panic
,如果想要通过这种测试,可以添加 should_panic
:
/// # Panics
///
/// The function panics if the second argument is zero.
///
/// ```rust,should_panic
/// // panics on division by zero
/// world_hello::compute::div(10, 0);
/// ```
通过 should_panic
,告诉 Rust 我们这个用例会导致 panic
,这样测试用例就能顺利通过。
保留测试,隐藏文档
在某些时候,我们希望保留文档测试的功能,但是又要将某些测试用例的内容从文档中隐藏起来:
rust
/// ```
/// # // 使用#开头的行会在文档中被隐藏起来,但是依然会在文档测试中运行
/// # fn try_main() -> Result<(), String> {
/// let res = world_hello::compute::try_div(10, 0)?;
/// # Ok(()) // returning from try_main
/// # }
/// # fn main() {
/// # try_main().unwrap();
/// #
/// # }
/// ```
pub fn try_div(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err(String::from("Divide-by-zero"))
} else {
Ok(a / b)
}
}
文档注释中的代码跳转
跳转到标准库
rust
/// `add_one` 返回一个[`Option`]类型
pub fn add_one(x: i32) -> Option<i32> {
Some(x + 1)
}
此处的 [Option
] 就是一个链接,指向了标准库中的 Option
枚举类型。
再比如,还可以使用路径的方式跳转:
rust
use std::sync::mpsc::Receiver;
/// [`Receiver<T>`] [`std::future`].
///
/// [`std::future::Future`] [`Self::recv()`].
pub struct AsyncReceiver<T> {
sender: Receiver<T>,
}
impl<T> AsyncReceiver<T> {
pub async fn recv() -> T {
unimplemented!()
}
}
使用完整路径跳转到指定项
除了跳转到标准库,你还可以通过指定具体的路径跳转到自己代码或者其它库的指定项,例如在 lib.rs
中添加以下代码:
rust
pub mod a {
/// `add_one` 返回一个[`Option`]类型
/// 跳转到[`crate::MySpecialFormatter`]
pub fn add_one(x: i32) -> Option<i32> {
Some(x + 1)
}
}
pub struct MySpecialFormatter;
使用 crate::MySpecialFormatter
这种路径就可以实现跳转到 lib.rs
中定义的结构体上。
同名项的跳转
rust
/// 跳转到结构体 [`Foo`](struct@Foo)
pub struct Bar;
/// 跳转到同名函数 [`Foo`](fn@Foo)
pub struct Foo {}
/// 跳转到同名宏 [`foo!`]
pub fn Foo() {}
#[macro_export]
macro_rules! foo {
() => {}
}
文档搜索别名
Rust 文档支持搜索功能,我们可以为自己的类型定义几个别名,以实现更好的搜索展现,当别名命中时,搜索结果会被放在第一位:
rust
#[doc(alias = "x")]
#[doc(alias = "big")]
pub struct BigX;
#[doc(alias("y", "big"))]
pub struct BigY;