关闭

Rust中文翻译18

标签: rust编程语言testing自动测试测试
663人阅读 评论(0) 收藏 举报
分类:

4.4 文档

文档对于任何代码来说都很重要,对于Rust来说是头等重要.我们来讨论一下Rust使用文档的工具.


关于rustdoc

Rust发行版包含一个工具,rustdoc,它可以生成文档.rustdoc也可以被Cargo使用,通过cargo doc命令.

文档可以通过2中方式生成:源码,独立的Markdown文件.

Page 85

从源码生成文档

生成文档的基本方方法就是从源代码的注释中生成.你可以使用文档注释:

/// Constructs a new `Rc<T>`.
///
/// # Examples
///
/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```
pub fn new(value: T) -> Rc<T> {
    // implementation goes here
}

这段代码生成了文档.我使用一个普通的注释来实现方法.第一个需要注意的事情是:他使用了///,而不是//.3斜杠意味着一个文档注释.
文档注释使用Markdown来书写.
Rust会跟踪这些注释,用他们来生成文档.当使用文档来标注枚举的时候这一方法很重要:

/// The Òption` type. See [the module level documentation](../) for more.
enum Option<T> {
    /// No value
    None,
    /// Some value `T`
    Some(T),
}

上面的代码可以工作,但是这却不行:

/// The Òption` type. See [the module level documentation](../) for more.
enum Option<T> {
    None, /// No value
    Some(T), /// Some value `T`
}

你会得到一个错误:


Page 86

这个不幸的错误其实是对的:文档注释应用于其后的代码,但是在最后一个注释之后什么都没有.

撰写文档注释
让我们分析一下这个文档注释的细节:

/// Constructs a new `Rc<T>`.
#fn foo() {}

第一行注释应该是一个功能简介.一句话.只有基本信息.高层次的.

///
/// Other details about constructing `Rc<T>`s, maybe describing complicated
/// semantics, maybe additional options, all kinds of stuff.
///
# fn foo() {}

我们的原始例子只有一行简介,我们可以另起一段多加一些注释.

特殊段落:

/// # Examples
# fn foo() {}

接下来是特殊段落.有#来指明.有3中常用的头.他们不是特殊语法,仅仅只是惯例.

/// # Panics
# fn foo() {}

无法挽回的函数误用在Rust中由panics指出,它会杀掉当前运行线程.如果你的函数有重要的功能,panics会检测到,所以文档是很重要的.

/// # Failures
# fn foo() {}

如果你的函数返回了Result<T, E>,那么描述一下返回Err(E)的时候的条件也是一件很好的事情.它的重要性仅次于Panics,因为失败也是编码进类型系统的.

/// # Safety
# fn foo() {}

如果你的代码不安全,你需要解释那个不变的调用者负责它的安全性.

/// # Examples
///
/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```
# fn foo() {}

第三,Examples.包含了多个你的行数或方法,你的使用者一定会喜欢他的.这些例子可以进入代码的区块注释中,稍后会将,并且可以有多个段落.

/// # Examples
///
/// Simple `&str` patterns:
///
/// ```
/// let v: Vec<&str> = "Mary had a little lamb".split(' ').collect();
/// assert_eq!(v, vec!["Mary", "had", "a", "little", "lamb"]);
/// ```
///
/// More complex patterns with a lambda:
///
/// ```
/// let v: Vec<&str> = "abc1def2ghi".split(|c: char| c.is_numeric()).collect();
/// assert_eq!(v, vec!["abc", "def", "ghi"]);
/// ```
# fn foo() {}

我们来讨论一下细节.

代码区块注释:
撰写Rust注释的,使用3斜杠:

Page 88

/// ```
/// println!("Hello, world");
/// ```
# fn foo() {}

如果你不想使某些东西成为Rust代码,你可以添加一个注释:

/// ```c
/// printf("Hello, world\n");
/// ```
# fn foo() {}

这些可以被高亮,取决于你的语言.如果你就像现实纯文本,那么选择text. 

选择正确的注释是很重要的,因为rustdoc使用这些注释的时候是用一种有意思的方式:它会真的被用来测试你的代码,所以他们不会过时.如果你有一些c代码而rustdoc认为它是Rust代码(因为你没有注释),那么rustdoc就会抱怨当他尝试形成文档的时候.

文档中的测试:

我们来讨论一下我们的文档:

/// ```
/// println!("Hello, world");
/// ```
# fn foo() {}

你会注意到你不必写main()函数.rustdoc会自动为你生成一个main()函数.例如:
/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```
# fn foo() {}

这样就会结束测试:

fn main() {
    use std::rc::Rc;
    let five = Rc::new(5);
}

这些是rustdoc处理示例的算法:
1.任何#![foo]开始的行都被当作crate的属性.
2.一些allow属性会插入进来,包括unused_variables, unused_assignments, unused_mut,  unused_attributes, 和dead_code.小的示例经常会触发这些检测.
3.如果示例没有包含extern crate,那么extern crate <mycrate>;就会被插入.
4.最后,如果示例没有包含fn main,余下的文本就会被fn main {your_code}包含.

这些还不够.例如,我们之前讨论过的以///开头的例子,原始文本是这样的:

/// Some documentation.
# fn foo() {}

是的,你可以添加任意行,以#开头,他们会在输出中被隐藏起来,但是在编译时会被使用.你可以用这来实现你的高级功能.在此例中,文档注释被用来应用于某些函数,所以如果我只想给你看我文档注释,我需要添加一个小函数.同时,只有此处才能满足编译器,隐藏他们会让示例变得清晰.你可以使用这个技术来解释复杂的例子,例如:

let x = 5;
let y = 6;
println!("{}", x + y);

这里是解释:
首先,我们将x赋值为5:

let x = 5;
# let y = 6;
# println!("{}", x + y);

接着,我们给y赋值为6:
# let x = 5;
let y = 6;
# println!("{}", x + y);

最后,我们打印和:
# let x = 5;
# let y = 6;
println!("{}", x + y);

通过重复示例中的各个部分,你可以确保你的代码仍然能够通过编译,而只显示出你想要解释的内容.

文档宏:
这有一个文档宏的例子:

/// Panic with a given message unless an expression evaluates to true.
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate foo;
/// # fn main() {
/// panic_unless!(1 + 1 == 2, “Math is broken.”);
/// # }
/// ```
///
/// ```should_panic
/// # #[macro_use] extern crate foo;
/// # fn main() {
/// panic_unless!(true == false, “Iʼm broken.”);
/// # }
/// ```
#[macro_export]
macro_rules! panic_unless {
    ($condition:expr, $($rest:expr),+) => ({ if ! $condition { panic!($($rest),+); } });
}
# fn main() {}

你可以看到3件事情:我们需要添加我们的extern crate行,这样才能添加#[marco_use]属性.第二,我们需要自己添加main()函数.最后,一个聪明的#注释了这两件事情,从而他们不会被输出.

运行文档测试:
运行文档测试,你可以:
$rustdoc --test path/to/my/crate/root.rs
#or
$carog test

是的,cargo test会测试内嵌的文档.然而,cargo test不会测试二进制的crate,而只测试库.这是因为rustdoc的工作方式:它会连接被测试库,但是对于二进制,没有任何链接.

还有一些有用的注释来让rustdoc做正确的事情:
/// ```ìgnore
/// fn foo() {
/// ```
# fn foo() {}

ignore引导会告诉Rust忽略你的代码.这基本不是你想要的.相反,如果不是代码就用text来注释,或者使用#来获得可以工作的示例.

/// ```should_panic
/// assert!(false);
/// ```# fn foo() {}

should_panic告诉rustdoc代码应该编译正确,但是不会通过测试.

/// ```no_run
/// loop {
///
println!("Hello, world");
/// }
/// ```
# fn foo() {}

no_run属性会编译你的代码,但是不会运行它.这一点在这些例子中很有用:"这里是如何启动一个网络服务",也就是逆向编译你的代码,但是可能会运行在一个无限循环中!

文档模块:
Rust还有一种文档注释,//!.这种注释不会为他们后面的内容形成文档,而是被他们包含的内容.也就是说:

mod foo {
//! This is documentation for the `foo` module.
//!
//! # Examples
// ...
}

这就是你最常见到//!的地方:一个模块的注释.如果你在foo.rs中有一个模块,你会经常看到这个:
//! A module for using `foo`s.
//!
//! The `foo` module contains a lot of useful functionality blah blah blah

文档注释的风格:
查看RFC505,里面有关于文档注释的所有格式和惯例风格.

其他文档:
所有这些行为在非Rust代码文件中也同样适用.因为注释是用Markdown写的,他们通常是.md文件.
当你在md文件中写文档是,你不需要文档注释前缀.例如:
/// # Examples
///
/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```
# fn foo() {}

就是:
use std::rc::Rc;
let five = Rc::new(5);

当在md文件中时.尽管这有一个小缺点:Markdown文件需要一个标题:
%The title
这个%行必须是文件的第一行.

文档属性:

在更深层次,文档注释就是文档属性的语法糖:
/// this
# fn foo() {}
#[doc="this"]
# fn bar() {}

和这个一样:
//! this
#![doc="/// this"]

你不会经常在文档注释中这样写,但是当改变某些属性的时候或者写一个宏的时候,这个有点用.

Re-exports:
rustdoc会将文档显示为一个public re-exports:
extern crate foo;
pub use foo:bar;

这会生成bar的文档在crate foo和你自己的crate里都会生成.两个地方会使用同样的文档.
这个行为可以被no_inline抵消:
extern crate foo;
#[doc(no_inline)]
pub use foo::bar;

控制HTML:
你可以通过#![doc]属性来控制一些HTML:
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
    html_favicon_url = "http://www.rust-lang.org/favicon.ico",
    html_root_url = "http://doc.rust-lang.org/")];

它设置了一些不同的选项,包括logo,favicon和一个跟url.

生成选项:

rustdoc还有一些命令行选项,来进一步定制化:
  • --html-in-header FILE:包含FILE文件的内容在<head>...</head>的结尾
  • --html-before-content FILE:包含FILE文件的内容在<body>的后面,渲染内容的前面.
  • --html-after-content FILE:包含FILE文件的内容在所有渲染内容的后面
安全注释:
文档注释中的Markdown不会处理最终的web页面.小心处理HTML:
/// <script>alert(document.cookie)</script>
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:32775次
    • 积分:696
    • 等级:
    • 排名:千里之外
    • 原创:4篇
    • 转载:0篇
    • 译文:33篇
    • 评论:13条
    文章分类
    最新评论