当前位置: 首页 > news >正文

Rust 入门 注释和文档之 cargo doc (二十三)

好的代码会说话,好的程序员不写注释,这些都是烂大街的“编程界俚语”,但是,如果你真的遇到一个不写注释的项目或程序员,。。。

在之前的章节,我们学习来包和模块如何使用,在此章节将进一步学习如何书写文档注释,以及如何使用 cargo doc 生成项目的文档。 最后将以一个包,模块和文档的综合例子,来将这些知识融合贯通

注释的种类

在Rust中,注释分为三类

        代码注释,用于说明某以uaidaima的躬耕你,读者往往是同一个项目的协作开发者

        文档注释,支持Markdown ,对项目描述,公共API 等用户关心的功能进行介绍,同时还能提供示例代码,目标读者往往是想要了解项目的人

        包和模块注释,严格来说这也是文档注释中的一种,它主要用于说明当前包和模块的共轭能,方便用户迅速了解一个项目

通过这些注释,实现来Rust 极其优秀的文档化支持,甚至你还能在文档注释中写测试用例,省去来单独写测试用例的环节。

代码注释

显然之前的刮目相看是打了引号的, 想要去掉引号,该写注释的时候,就老老实实的,不过写时需要循序八字原则: 围绕目标,言简意赅, 

代码注释方式有两种

行注释 //

fn main() {// 我是 Sun...// face let name = "sunface";let age = 18; // 今年好像是 18岁
}

如上所示,行注释可以放在某一行代码的上方,也可以放在当前代码行的后方。如果超出一行的长度,需要在新行的开头也加上 // 

当注释行数较多时,还可以使用块注释

块注释/*....... */

fn main() {/*  xxxx*/let name = "sunface";let age = "???"; // 今年其实。。。挺大了}

如上所示,只需要将注释内容使用 /* */ 进行包裹即可

文档注释

当查看一个 crate.io 上的包时,往往需要通过它提供的文档来浏览相关的功能特性,使用方式,这种文档就是通过文档注释实现的。

Rust 提供了 cargo doc 的命令,可以用于把这些文档注释转换成 HTML 网页文件,最终展示给用户浏览, 这样 用户就知道这个包做什么的 以及该如何使用。

文档行注释///

开门见山

/// `add_one` 将指定值加1
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {x + 1
}

以上代码有几点需要注意

        文档注释需要位于 lib 类型的包中, 例如 src/lib.rs 中

        文档注释可以使用markdown 语法,例如 # Examples 的标题,以及代码块高亮

        被注释的对象需要使用 pub 对外可见,记住,文档注释是给用看的, 内部实现细节不应该被暴露出去

文档注释中的例子,为什么看上去显示能运行的颜值么? 竟然还是有 assert_eq 这中常用于测试目的的宏。

文档块注释 /** ... */

与代码注释一样,文档也有块注释,当注释内容多是,使用块注释可以减少 /// 的使用:

/** `add_two` 将指定值加2```
let arg = 5;
let answer = my_crate::add_two(arg);assert_eq!(7, answer);
```
*/
pub fn add_two(x: i32) -> i32 {x + 2
}

查看文档 cargo doc 

锦衣不夜行,我们写了这么漂亮的文档注释,当然要看看网页中是什么效果。

很简单,运行 cargo  doc 可以直接生成 HTML 文档,放入taget/doc 目录下

当然,为来方便,我们使用 cargo doc --open 命令,可以在生成文档后,自动在浏览器中打开网页,最终效果如图所示

常用文档标题

之前我们见到来在文档注释中该如何使用 markdown ,其中包括 # Examples 标题,还有一些常用的,你可以在项目中酌情是哦哪个

        Panics: 函数可能会出现的异常情况,这样调用函数的人就可以提前规避

        Errors  描述可能出现的错误及什么情况会导致错误,有助于调用者针对不同的错误采取不同的处理方式

        Safety  如果函数使用unsafe 代码, 那么调用者就需要注意一些使用条件,以确保unsafe 代码块的正常工作。

话说回来, 这些标题更多的是一种惯例, 如果你非要用中文标题也没问题,但是最好在团队中保持同样的风格

包和模块级别的注释

除了函数,结构体等Rust项的注释,你还可以给包和模块添加注释,需要注意的是, 这些注释要添加到包、模块的最上方!

与之前的任何注释一样,包级别的注释也分为两种 : 行注释 //!  和 块注释 /*! ... */.

现在,为我们的包增加注释,在 src/lib.rs 包根的最三方,添加:

/*! lib包是world_hello二进制包的依赖包,里面包含了compute等有用模块 */pub mod compute;

然后在为该包根的子模块 src/compute.rs 添加注释

//! 计算一些你口算算不出来的复杂算术题


/// `add_one`将指定值加1
///

运行 cargo doc  --open 看效果

包模块注释,可以让用户从整体的角度理解包的用户,对于用户来说非常友好的,就和一篇文章的开头一样,总是要对文章的内容进行大致的介绍。让用户在看的时候心中有数。

至此,关于如何注释的内容,就结束了,还有其它的用法, 一起来看看。

文档测试(Doc Test)

相信读者之前都写过单元测试用例,其中一个很蛋疼的问题就是,随着代码的进化,单元测试用例经常会失败,过段时间后, 你发项需要连续修改不少处代码,才能让测试重新工作起来,然而,在Rust中, 大可不必。

在之前的 add_one 中,我们写的示例代码非常像是一个单元测试的用例,这是偶然吗? 并不是,因为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

可以看到,文档中的测试用例被完美运行,而且输出中也明确提示了 Doc-tests world_hello ,意味着这些测试的名字交错 Doc test 文档测试。

需要注意的是,你可能需要使用类如 world_hello::compute::add_one(arg) 的完整路径来调用函数,因为测试是在另外一个独立的线程中运行的

造成 panic 的文档测试

文档测试中的用例还可以造成 panic :

/// # 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 : 

---- src/compute.rs - compute::div (line 38) stdout ----
Test executable failed (exit code 101).

stderr:
thread 'main' panicked at 'Divide-by-zero error', src/compute.rs:44:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

如果想要通过这种测试,可以添加 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 ,这样测试用例就能顺利通过。

保留测试,隐藏文档

在某些时候,我们希望保留文档测试的功能, 但是又要将某些测试用例的内容从文档中隐藏起来

/// ```
/// # // 使用#开头的行会在文档中被隐藏起来,但是依然会在文档测试中运行
/// # 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)}
}

以上文档注释中,我们使用 # 将不想让用户看到的内容隐藏起来,但是又不影响测试用例的运行,最终用户将只能看到那行没有隐藏的 let res = wold_hello::compute::try_div(10,0)?; :

文档注释中的代码跳转

Rust在文档注释中还提供来一个非常强大的功能,那就是可以实现对外部项的链接

跳转到标准库

/// 'add_one' 返回一个 ['Option'] 类型
pub fn add_one(x: i32) -> Option<i32> {Some(x + 1)
}

此处的[option] 就是一个链接, 指向来标准库中的 Option 枚举类型, 有两种方式可以进行跳转

        在 IDE 中,使用 Command + 鼠标左键(macOS), CTRL + 鼠标左键(Windows)

        在文档中直接点链接

再比如,还可以使用路径的方式跳转:

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 中添加以下代码:

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 中定义的结构体上。

同名项的跳转

如果遇到同名项,可以使用标示类型的方式进行跳转

/// 跳转到结构体  ['Foo'](struct@Foo)
pub struct Bar;/// 跳转到同名函数 ['Foo'](fn@Foo)
pub struct Foo {}/// 跳转到同名宏 ['foo!']
pub fn Foo() {}#[macro_export]
macro_rules! foo {() => {}
}

文档搜索别名

Rust 文档至此搜索功能, 我们可以为自己的类型定义几个别名,以实现更好的搜索展现,当别名命中时,搜索结果会被放在第一位:

#[doc(alias = "x")]
#[doc(alias = "big")]
pub struct BigX;#[doc(alias("y","big"))]
pub struct BigY;

一个综合例子

这个例子我们将重点应用几个知识点:

        文档注释

        一个项目可以包含两个包: 二进制可执行包和 lib 包 (库包) ,它们的包根分别是 src/main.rs 和 src/lib.rs

        在二进制包中引用lib 包

        使用 pub use 再导出 API ,并观察文档

首先,使用 cargo new art 创建一个 Package art;

Created binary (application) `art` package

系统提示我们创建了一个二进制 Package , 根据之前章节学过的内容,可以知道该 Package 包含一个同名的二进制包: 包名为art  包根为 src/main.rs , 该包可以编译成二进制然后运行。

现在,在src 目录下创建一个 lib.rs 文件, 根据之前学习的知识,创建该文件等于又创建了一个库类型的包, 包名也是 art ,包根为 src/lib.rs ,该包是库类型的,因此往往作为依赖库被引入

将以下内容添加到 src/lib.rs 中 

//! # Art 
//!
//!  未来的艺术建模库,现在的调色库pub use self::kinds::PrimaryColor;
pub use self::kinds::SecondaryColor;
pub use self::utils::mix;pub mod kinds {//! 定义颜色的类型/// 主色pub  enum PrimaryColor {Red,Yellow,Blue,}/// 副色#[derive(Debug,PartialEq)]pub enum SecondaryColor {Orange,Green,Purple,}
}pub mod utils {//! 实用工具,目前只实现了调色板use crate::kinds::*;/// 将两种主色调成副色/// '''rust/// use art::utils::mix;/// use art::kinds::{PrimaryColor,SecondaryColor);/// assert!(matches!(mix(PriamaryColor::Yellow,PrimaryColor::Blue),SecondaryColor::Green));/// '''pub fn mix(c1:PrimaryColor,c2:PrimaryColor) -> SecondaryColor{SecondaryColor::Green}
}

在库包的包根 src/lib.rs 下, 我们又定义了几个子模块,同时将子模块中的三个项通过 pub use 进行了在导出

接着,将下面内容添加到 src/main.rs 中

use art::kinds::PrimaryColor;
use art::utils::mix;fn main() {let blue = PrimaryColor::Blue;let yellow =PrimaryColor::Yellow;println!("{:?}",mix(blue,yellow));
}

在二进制可执行包的包根 src/main.rs 下, 我们引入了库包 art 中的模块项,同时使用main 函数作为程序的入口,该二进制包可以使用 cargo run 运行

Green

至此,库包完美提供了用于调色的api ,二进制包引入这些API 完美的实现了料色并打印输出。

总结

在Rust 中, 注释分为主要三种类型: 代码注释,文档注释,包和模块注释,每个注释类型都拥有两种形式,行注释和块注释,熟练掌握包模块和注释的知识,非常有主语我们创建工程性更强的项目。

http://www.dtcms.com/a/346170.html

相关文章:

  • 51单片机-中断系统
  • 【数据分享】各省及全国GDP增长指数(1980-2022)
  • 彻底解决 Windows 文件扩展名隐藏问题,注册表修改显示文件后缀方法
  • More Effective C++ 条款01:仔细区别 pointers 和 references
  • 构建城市数字孪生底座:深度解析智慧城市全景视频拼接融合解决方案
  • constraint_mode使用
  • 【Python】两条命令永久切国内源
  • Android 16环境开发的一些记录
  • C语言中的CSI_START和CSI_END宏
  • 拿到手一个前端项目,应该如何启动
  • 多目标跟踪中基于目标威胁度评估的传感器控制方法复现
  • lanczos算法学习笔记
  • 【GM3568JHF】FPGA+ARM异构开发板 测试命令
  • OFD格式文件及Python将PDF转换为OFD格式文件
  • Informer参数代码
  • SPI的DMA方式
  • 线性回归:从原理到实战的完整指南
  • ROS中的自定义消息
  • Windows 11 安装 Miniforge,配置国内源
  • 基层医疗遇到了什么问题?
  • 【spring security】权限管理组件执行流程详解
  • centos7安装oracle19c流程(自用)
  • Highcharts 推出适用于 Svelte 的官方集成库
  • 【软考架构】关系数据库
  • 无人机电机与螺旋桨的匹配原理及方法(一)
  • 随机森林--集成学习
  • 华为网路设备学习-29(BGP协议 四)路由策略-实验
  • 虚拟线程(高版本JDK)
  • 在 SymPy 中代入抽象函数的数值和导数值
  • JSP入门详解