标签:rust 编程语言 testing 自动测试 测试
文档对于任何代码来说都很重要,对于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>
Rust中文翻译18
标签:rust 编程语言 testing 自动测试 测试
原文地址:http://blog.csdn.net/zcmit/article/details/46829045