====== 测试与高效 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'' 等工具