====== 测试与高效 Rust 实践 ======
===== 编写测试 =====
Rust 内置测试框架,无需额外依赖。
==== 测试注解 ====
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
#[test]
fn it_fails() {
panic!("这个测试会失败");
}
}
==== 常用断言宏 ====
^ 宏 ^ 说明 ^
| ''assert!(expr)'' | 断言表达式为 true |
| ''assert_eq!(a, b)'' | 断言 a == b |
| ''assert_ne!(a, b)'' | 断言 a != b |
fn add_two(x: i32) -> i32 {
x + 2
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_two() {
assert_eq!(4, add_two(2));
assert_ne!(5, add_two(2));
}
#[test]
fn test_with_message() {
let result = add_two(3);
assert!(
result > 0,
"结果应为正数,但得到 {}", result
);
}
}
==== 测试 panic ====
用 ''#[should_panic]'' 测试应该 panic 的函数:
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("除数不能为零");
}
a / b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic(expected = "除数不能为零")]
fn test_divide_by_zero() {
divide(10, 0);
}
}
==== 使用 Result 测试 ====
#[cfg(test)]
mod tests {
#[test]
fn test_with_result() -> Result<(), String> {
if 2 + 2 == 4 {
Ok(())
} else {
Err("2+2 不等于 4".to_string())
}
}
}
==== 运行测试 ====
cargo test # 运行所有测试
cargo test test_name # 运行指定测试
cargo test -- --nocapture # 显示 println! 输出
cargo test -- --test-threads=1 # 单线程运行
===== 常用标准库 Trait =====
==== 自动派生 ====
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 0, y: 0 };
let p2 = p1.clone();
println!("{:?}", p1); // Debug 格式化
println!("{}", p1 == p2); // PartialEq 比较
}
==== 格式化 ====
use std::fmt;
struct Person {
name: String,
age: u8,
}
impl fmt::Display for Person {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}({}岁)", self.name, self.age)
}
}
fn main() {
let p = Person {
name: "小明".into(),
age: 25,
};
println!("{}", p); // "小明(25岁)"
}
===== 条件编译 =====
#[cfg(target_os = "windows")]
fn platform_function() {
println!("这是 Windows 系统");
}
#[cfg(target_os = "linux")]
fn platform_function() {
println!("这是 Linux 系统");
}
#[cfg(debug_assertions)]
fn debug_only() {
println!("仅在调试模式下编译");
}
fn main() {
platform_function();
// 调试模式下才执行
if cfg!(debug_assertions) {
println!("调试模式");
}
}
===== 常用实践建议 =====
==== 使用 Option 和 Result ====
优先使用 ''Option'' 和 ''Result'' 代替需要 ''null'' 或抛出异常的设计。
==== 避免 panic! ====
库代码中避免使用 ''panic!'',返回 ''Result'' 让调用者决定如何处理错误。
==== 使用迭代器 ====
优先使用迭代器链式操作代替手写循环,代码更简洁且性能不输手写。
// 推荐
let sum: i32 = numbers.iter().filter(|x| x % 2 == 0).sum();
// 不如推荐
let mut sum = 0;
for x in &numbers {
if x % 2 == 0 {
sum += x;
}
}
==== 命名规范 ====
* 类型、Trait:大驼峰 ''MyStruct''
* 函数、变量、模块:蛇形命名 ''my_function''
* 常量:全大写下划线 ''MAX_VALUE''
==== 文档注释 ====
/// 计算两个数的和
///
/// # 示例
///
/// ```
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
fn add(a: i32, b: i32) -> i32 {
a + b
}
''cargo doc --open'' 生成 HTML 文档。
===== 本章实践 =====
/// 计算斐波那契数列第 n 项(测试驱动开发)
fn fib(n: u32) -> u32 {
match n {
0 => 0,
1 => 1,
_ => fib(n - 1) + fib(n - 2),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fib() {
assert_eq!(fib(0), 0);
assert_eq!(fib(1), 1);
assert_eq!(fib(2), 1);
assert_eq!(fib(3), 2);
assert_eq!(fib(10), 55);
}
#[test]
fn test_fib_large() {
assert_eq!(fib(20), 6765);
}
}
===== 本章小结 =====
* ''#[test]'' 注解编写测试,''cargo test'' 运行
* ''assert!''、''assert_eq!''、''assert_ne!'' 断言
* ''#[should_panic]'' 测试错误路径
* ''#[derive(Debug, Clone, PartialEq)]'' 自动派生常用 trait
* ''#[cfg]'' 条件编译
* 善用 ''cargo doc''、''cargo test''、''cargo clippy'' 等工具