Rust: Rust Testing and Documentation
Last updated: 2026-08-26
Testing is the "quality inspector" of code—let testing find bugs before users do. Documentation is the "instruction manual" of code—it not only tells others how to use it, but also reminds your future self why you wrote it that way.
If writing code is like "building a house," then testing is the "quality inspection report," and documentation is the "user manual." Would you dare to move into a house that hasn't been tested? The core concept of TDD (Test-Driven Development) is simple: write the tests first, then the code—just like setting quality standards before manufacturing a product.
1. What You'll Learn
#[test]Property marker test function,cargo testRun test suiteassert!/assert_eq!/assert_ne!Assertion macros to verify expected behavior#[should_panic]Testing expected panic scenarios#[cfg(test)]Conditional compilation test module; automatically excluded from production builds///Document Annotations andcargo docGenerating HTML Documents- Integration Testing (in the
tests/directory) and Benchmark Testing Concepts
2. The Story of a Quality Inspector
(1) The Struggle: Factories Without Quality Control
Bob is a quality control inspector at an auto plant. Shortly after starting his job, he discovered a shocking fact:
- The factory produces 500 cars a day, but 0 of them pass quality inspection
- A customer complained, "I just bought a car, and the steering wheel is square!"
- The quality control supervisor said, "It's okay; the customer will fix it themselves."
- Even more absurd is that the factory's "instructions" consist of a crumpled piece of paper that reads: "Step on the round one, turn the long one."
"If every car were tested before leaving the factory, customers wouldn't end up with square steering wheels..."
(2) Approaches to Testing and Documentation in Rust
Rust provides a complete set of testing and documentation tools, much like the quality management system of a well-run factory:
Factory(Rust Project) → Automobile Production Line
Unit Testing(Unit Test) → Check the quality of each part
Document Testing(Doc Test) → Examples included in the manual
Integration Testing(Integration Test)→ Vehicle Road Testing
Benchmarking(Benchmark) → Performance Testing
Document Notes(cargo doc) → Complete User Manual
/// Calculate the greatest common divisor of two numbers(GCD)
///
/// Using the Euclidean Algorithm:Repeat the modulo operation until the remainder is 0
///
/// # Example
///
/// ```
/// let result = gcd(12, 8);
/// assert_eq!(result, 4);
/// ```
fn gcd(a: u64, b: u64) -> u64 {
if b == 0 { a } else { gcd(b, a % b) }
}
#[test]
fn test_gcd() {
assert_eq!(gcd(12, 8), 4);
assert_eq!(gcd(7, 13), 1); // Coprime Numbers
assert_eq!(gcd(100, 10), 10);
}
Rust's testing framework is like a quality control assembly line:
#[test]marks each "quality control station,"cargo teststarts the entire assembly line, andassert_eq!represents the quality control standards.///documentation comments are like the "instructions" attached to each part, andcargo docbinds all the instructions into a single booklet.
3. Core Concepts
(1) Testing and Documentation Framework
graph TB
A[Rust Testing and Documentation] --> B[Test System]
A --> C[Document Management System]
B --> B1["#[test] Mark the test function"]
B --> B2["cargo test Run Test"]
B --> B3["Assertion Macro assert! / assert_eq! / assert_ne!"]
B --> B4["#[should_panic] Test Anxiety"]
B --> B5["#[cfg(test)] Conditional Compilation"]
B --> B6["tests/ Integration Test Directory"]
C --> C1["/// Document Notes"]
C --> C2["cargo doc Generate HTML"]
C --> C3["Document Testing Doc-tests"]
C --> C4["cargo doc --open Open your browser"]
B1 --> D1[Unit Testing]
B6 --> D2[Integration Testing]
C1 --> D3[API Document]
C3 --> D4[Working sample code]
(2) Comparison of Test Types
| Test Type | Keyword/Location | Test Scope | Analogy | Applicable Scenarios |
|---|---|---|---|---|
| Unit Testing | #[test] + #[cfg(test)] |
Individual functions or modules | Check each component | Verify function logic |
| Document Testing | Code blocks in /// |
API sample code | Examples included in the manual | Ensure that the examples in the documentation are functional |
| Integration Testing | The .rs file in the tests/ directory |
Overall behavior of external APIs | Vehicle road testing | Verification of inter-module collaboration |
| Benchmark | #[bench] / cargo bench |
Performance Metrics | Speed Test | Performance-Sensitive Code |
(3) Comparison of Assertion Macros
| Macro | Purpose | Success Conditions | Failure Message |
|---|---|---|---|
assert!(expr) |
Boolean Conditions | expr == true |
assertion failed: expr |
assert_eq!(a, b) |
Equivalence | a == b |
assertion failed: (left == right) |
assert_ne!(a, b) |
Asymmetry | a != b |
assertion failed: (left != right) |
4. Testing and Documentation Examples
▶ Example 1: Unit Tests and Assertion Macros (Difficulty ⭐)
Output:
=== Calculator Feature Demo ===
add(10, 5) = <add(10, 5)>
subtract(10, 5) = <subtract(10, 5)>
divide(10, 3) = <divide(10, 3)>
is_even(7) = <is_even(7)>
max_of_three(3, 7, 5) = <max_of_three(3, 7, 5)>
Run `cargo test` Run all the tests
// ============================================
// Unit Testing Basics: #[test] + Assertion Macro
// Demo: A Simple Calculator Function and Its Tests
// Quality control inspectors check each calculation function
// ============================================
/// Addition: Returns a + b
fn add(a: i32, b: i32) -> i32 {
a + b
}
/// Subtraction: Returns a - b
fn subtract(a: i32, b: i32) -> i32 {
a - b
}
/// Division: Returns a / b, panics if b == 0
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("division by zero is not allowed!");
}
a / b
}
/// Determining Whether a Number Is Even
fn is_even(n: i32) -> bool {
n % 2 == 0
}
/// Find the Maximum Value
fn max_of_three(a: i32, b: i32, c: i32) -> i32 {
let mut max = a;
if b > max { max = b; }
if c > max { max = c; }
max
}
fn main() {
println!("=== Calculator Feature Demo ===");
println!("add(10, 5) = {}", add(10, 5));
println!("subtract(10, 5) = {}", subtract(10, 5));
println!("divide(10, 3) = {}", divide(10, 3));
println!("is_even(7) = {}", is_even(7));
println!("max_of_three(3, 7, 5) = {}", max_of_three(3, 7, 5));
println!();
println!("Run `cargo test` Run all the tests");
}
// ============================================
// Test Module —— Only at cargo test Compiled by Shi
// ============================================
#[cfg(test)]
mod tests {
// Import all functions from the parent module
use super::*;
#[test]
fn test_add_positive() {
assert_eq!(add(2, 3), 5);
}
#[test]
fn test_add_negative() {
assert_eq!(add(-2, -3), -5);
}
#[test]
fn test_add_zero() {
assert_eq!(add(0, 0), 0);
}
#[test]
fn test_subtract() {
assert_eq!(subtract(10, 4), 6);
assert_eq!(subtract(4, 10), -6); // Negative results
}
#[test]
fn test_divide_normal() {
assert_eq!(divide(10, 3), 3); // Truncated Integer Division
}
#[test]
#[should_panic(expected = "division by zero is not allowed!")]
fn test_divide_by_zero() {
divide(10, 0); // Expectations panic
}
#[test]
fn test_is_even() {
assert!(is_even(4)); // 4 It is an even number
assert!(!is_even(7)); // 7 Not an even number
assert_eq!(is_even(0), true);
}
#[test]
fn test_max_of_three() {
assert_eq!(max_of_three(1, 2, 3), 3);
assert_eq!(max_of_three(5, 1, 2), 5);
assert_eq!(max_of_three(1, 5, 2), 5);
assert_eq!(max_of_three(-1, -5, -3), -1);
}
#[test]
fn test_assert_ne_macro() {
// assert_ne! Verify that two values are not equal
assert_ne!(add(1, 1), 3);
assert_ne!(subtract(100, 50), 100);
}
}
Output:
=== Calculator Feature Demo ===
add(10, 5) = 15
subtract(10, 5) = 5
divide(10, 3) = 3
is_even(7) = false
max_of_three(3, 7, 5) = 7
Run `cargo test` Run all the tests
Test Output (cargo test):
running 9 tests
test tests::test_assert_ne_macro ... ok
test tests::test_add_negative ... ok
test tests::test_add_positive ... ok
test tests::test_add_zero ... ok
test tests::test_divide_by_zero ... ok
test tests::test_divide_normal ... ok
test tests::test_is_even ... ok
test tests::test_max_of_three ... ok
test tests::test_subtract ... ok
test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
The test module is wrapped in
#[cfg(test)], which means "compile only in test mode"—this code will not be present at all in production builds (cargo build --release).use super::*imports all functions from the parent module (i.e., the main code in the current file).#[should_panic(expected = "...")]verifies that a function will correctly trigger a panic under specific conditions.
▶ Example 2: Document Annotations and cargo doc (Difficulty: ⭐⭐)
Output:
=== String Library Demo ===
reverse('rust') = '<reverse("rust")>'
word_count('hello world from Rust') = <word_count("hello world from Rust")>
truncate('hello world', 5) = '<truncate("hello world", 5)>'
truncate('Hello, World', 2) = '<truncate("Hello, World", 2)>'
is_palindrome('racecar') = <is_palindrome("racecar")>
is_palindrome('A man a plan a canal Panama') = <is_palindrome("A man a plan a canal Panama")>
is_palindrome('hello') = <is_palindrome("hello")>
Usage `cargo doc --open` Generate and View HTML Document
Usage `cargo test` Run the documentation tests (doc-tests)
// ============================================
// Document Notes /// and cargo doc Generate HTML Document
// Demo: Write a complete API Document
// Includes document testing (doc-tests)
// ============================================
/// String Processing Toolkit
///
/// Provides common string manipulation functions,Including reversals、Excerpt、Statistics, etc.。
/// All functions are pure functions (do not modify the input, return a new value).
///
/// # Example
///
/// ```
/// use docs_demo::string_utils;
///
/// let reversed = string_utils::reverse("hello");
/// assert_eq!(reversed, "olleh");
/// ```
pub mod string_utils {
/// Reverse a String
///
/// Reverse the order of the characters in the input string。
///
/// # Parameters
///
/// * `s` - The string slice to be reversed
///
/// # Return Value
///
/// Return the new value after reversal `String`。
///
/// # Example
///
/// ```
/// use docs_demo::string_utils::reverse;
///
/// assert_eq!(reverse("rust"), "tsur");
/// assert_eq!(reverse(""), "");
/// assert_eq!(reverse("a"), "a");
/// ```
pub fn reverse(s: &str) -> String {
s.chars().rev().collect()
}
/// Count the number of words in a string
///
/// Count the number of non-empty words, separated by spaces。
///
/// # Parameters
///
/// * `s` - Strings to be counted
///
/// # Return Value
///
/// Back `usize` Number of Words by Type。
///
/// # Example
///
/// ```
/// use docs_demo::string_utils::word_count;
///
/// assert_eq!(word_count("hello world"), 2);
/// assert_eq!(word_count(""), 0);
/// assert_eq!(word_count(" spaces "), 0); // Only spaces
/// ```
pub fn word_count(s: &str) -> usize {
s.split_whitespace().count()
}
/// Extract the first part of a string n characters
///
/// Safe Handling UTF-8 Character Boundary,No panic。
/// If `n` Greater than the string length,Return the entire string。
///
/// # Parameters
///
/// * `s` - Original string
/// * `n` - Number of characters to extract
///
/// # Return Value
///
/// Returns the sliced string。
///
/// # Example
///
/// ```
/// use docs_demo::string_utils::truncate;
///
/// assert_eq!(truncate("hello world", 5), "hello");
/// assert_eq!(truncate("Hello", 2), "He");
/// assert_eq!(truncate("short", 100), "short");
/// ```
pub fn truncate(s: &str, n: usize) -> &str {
// Usage char_indices Safe Handling UTF-8 Boundary
if n >= s.chars().count() {
return s;
}
let end = s.char_indices()
.nth(n)
.map(|(idx, _)| idx)
.unwrap_or(s.len());
&s[..end]
}
/// Check if a string is a palindrome
///
/// Ignore case and spaces,Compare only alphanumeric characters。
///
/// # Parameters
///
/// * `s` - String to be checked
///
/// # Return Value
///
/// Returns true if the string is a palindrome `true`。
///
/// # Example
///
/// ```
/// use docs_demo::string_utils::is_palindrome;
///
/// assert!(is_palindrome("racecar"));
/// assert!(is_palindrome("A man a plan a canal Panama"));
/// assert!(!is_palindrome("hello"));
/// ```
pub fn is_palindrome(s: &str) -> bool {
let cleaned: String = s.chars()
.filter(|c| c.is_alphanumeric())
.map(|c| c.to_ascii_lowercase())
.collect();
cleaned == cleaned.chars().rev().collect::<String>()
}
}
fn main() {
use string_utils::*;
println!("=== String Library Demo ===");
println!("reverse('rust') = '{}'", reverse("rust"));
println!("word_count('hello world from Rust') = {}", word_count("hello world from Rust"));
println!("truncate('hello world', 5) = '{}'", truncate("hello world", 5));
println!("truncate('Hello, World', 2) = '{}'", truncate("Hello, World", 2));
println!("is_palindrome('racecar') = {}", is_palindrome("racecar"));
println!("is_palindrome('A man a plan a canal Panama') = {}", is_palindrome("A man a plan a canal Panama"));
println!("is_palindrome('hello') = {}", is_palindrome("hello"));
println!();
println!("Usage `cargo doc --open` Generate and View HTML Document");
println!("Usage `cargo test` Run the documentation tests (doc-tests)");
}
// ============================================
// Unit Testing —— Implementation of the Verification Function
// ============================================
#[cfg(test)]
mod tests {
use super::string_utils::*;
#[test]
fn test_reverse() {
assert_eq!(reverse("hello"), "olleh");
assert_eq!(reverse("Rust"), "tsuR");
assert_eq!(reverse(""), "");
}
#[test]
fn test_word_count() {
assert_eq!(word_count("one two three"), 3);
assert_eq!(word_count(""), 0);
assert_eq!(word_count(" "), 0);
assert_eq!(word_count("a"), 1);
}
#[test]
fn test_truncate() {
assert_eq!(truncate("hello world", 5), "hello");
assert_eq!(truncate("Hello, World", 2), "Hello");
assert_eq!(truncate("short", 10), "short");
}
#[test]
fn test_is_palindrome() {
assert!(is_palindrome("racecar"));
assert!(is_palindrome("level"));
assert!(is_palindrome("A man a plan a canal Panama"));
assert!(!is_palindrome("hello"));
assert!(is_palindrome(""));
}
}
Output:
=== String Library Demo ===
reverse('rust') = 'tsur'
word_count('hello world from Rust') = 4
truncate('hello world', 5) = 'hello'
truncate('Hello, World', 2) = 'Hello'
is_palindrome('racecar') = true
is_palindrome('A man a plan a canal Panama') = true
is_palindrome('hello') = false
Usage `cargo doc --open` Generate and View HTML Document
Usage `cargo test` Run the documentation tests (doc-tests)
///Documentation comments use Markdown format and support sections such as# Examples,# Parameters, and# Return Value. Sample code in code blocks automatically becomes "doc-tests"—cargo testcompiles and runs these code blocks to ensure that the examples in the documentation are always functional.cargo doc --opengenerates beautiful HTML documentation and opens it in a browser.
▶ Example 3: Integration Testing and Test Organization (Difficulty ⭐⭐)
Output:
=== Statistical Tools Demo ===
Data: <&data[..]>
sum = <sum(&data)>
average = <average(&data)>
median = <median(&mut data.to_vec()).unwrap()>
min = <min(&data).unwrap()>
max = <max(&data).unwrap()>
Integration test files should be placed in tests/ Under the directory,For example:
tests/stats_integration_test.rs
Run `cargo test --test stats_integration_test` Test a Specific File
// ============================================
// Testing Organization: Module Testing + Integration Test Directory Structure
// Demo: A "Statistical Tools" Library Testing Layers
// Note: Integration Testing requires standalone files in the tests/ directory
// This example simulates two types of tests in a single file.
// ============================================
/// Statistical Tools: Calculate various statistical indicators
pub mod stats {
/// Calculate the sum of a set of numbers
pub fn sum(numbers: &[i32]) -> i32 {
numbers.iter().sum()
}
/// Calculate the average of a set of numbers
/// If the set is empty,Back 0.0
pub fn average(numbers: &[i32]) -> f64 {
if numbers.is_empty() {
return 0.0;
}
sum(numbers) as f64 / numbers.len() as f64
}
/// Calculate the median of a set of numbers
/// If the set is empty,Back None
pub fn median(numbers: &mut [i32]) -> Option<f64> {
if numbers.is_empty() {
return None;
}
numbers.sort();
let len = numbers.len();
if len % 2 == 0 {
// Even count: Take the average of the middle two
let mid = len / 2;
Some((numbers[mid - 1] + numbers[mid]) as f64 / 2.0)
} else {
// Odd count: Take the one in the middle
Some(numbers[len / 2] as f64)
}
}
/// Calculate the minimum value of a set of numbers
pub fn min(numbers: &[i32]) -> Option<i32> {
numbers.iter().min().copied()
}
/// Find the maximum value of a set of numbers
pub fn max(numbers: &[i32]) -> Option<i32> {
numbers.iter().max().copied()
}
}
fn main() {
use stats::*;
println!("=== Statistical Tools Demo ===");
let data = [3, 1, 4, 1, 5, 9, 2, 6];
println!("Data: {:?}", &data[..]);
println!("sum = {}", sum(&data));
println!("average = {:.2}", average(&data));
println!("median = {:.1}", median(&mut data.to_vec()).unwrap());
println!("min = {:?}", min(&data).unwrap());
println!("max = {:?}", max(&data).unwrap());
println!();
println!("Integration test files should be placed in tests/ Under the directory,For example:");
println!(" tests/stats_integration_test.rs");
println!("Run `cargo test --test stats_integration_test` Test a Specific File");
}
// ============================================
// Unit Testing
// ============================================
#[cfg(test)]
mod tests {
use super::stats::*;
#[test]
fn test_sum() {
assert_eq!(sum(&[1, 2, 3, 4, 5]), 15);
assert_eq!(sum(&[]), 0);
assert_eq!(sum(&[-1, 0, 1]), 0);
}
#[test]
fn test_average() {
let result = average(&[1, 2, 3, 4, 5]);
assert!((result - 3.0).abs() < f64::EPSILON);
assert_eq!(average(&[]), 0.0);
}
#[test]
fn test_median_odd() {
let mut data = [3, 1, 4, 1, 5];
assert_eq!(median(&mut data), Some(3.0));
}
#[test]
fn test_median_even() {
let mut data = [1, 2, 3, 4];
assert_eq!(median(&mut data), Some(2.5));
}
#[test]
fn test_median_empty() {
let mut data: [i32; 0] = [];
assert_eq!(median(&mut data), None);
}
#[test]
fn test_min_max() {
let data = [3, -1, 7, 0, 42, -5];
assert_eq!(min(&data), Some(-5));
assert_eq!(max(&data), Some(42));
}
}
// ============================================
// Simulated Integration Testing (In a real Rust project,
// This should go in a separate file within the tests/ directory)
// ============================================
// The following content simulates tests/stats_integration_test.rs:
//
// use my_stats_lib::stats;
//
// #[test]
// fn test_integration_sum_and_average() {
// let data = [10, 20, 30, 40, 50];
// assert_eq!(stats::sum(&data), 150);
// assert!((stats::average(&data) - 30.0).abs() < f64::EPSILON);
// }
//
// #[test]
// fn test_integration_median_workflow() {
// // Testing a Typical Data Analysis Workflow
// let mut data = [100, 5, 50, 25, 75];
// let med = stats::median(&mut data);
// assert_eq!(med, Some(50.0));
// }
//
// #[test]
// fn test_integration_empty_data() {
// let data: [i32; 0] = [];
// assert_eq!(stats::sum(&data), 0);
// assert_eq!(stats::average(&data), 0.0);
// assert_eq!(stats::min(&data), None);
// assert_eq!(stats::max(&data), None);
// }
Output:
=== Statistical Tools Demo ===
Data: [3, 1, 4, 1, 5, 9, 2, 6]
sum = 31
average = 3.88
median = 3.5
min = 1
max = 9
Integration test files should be placed in tests/ Under the directory,For example:
tests/stats_integration_test.rs
Run `cargo test --test stats_integration_test` Test a Specific File
Integration tests are located in the
tests/folder within the project's root directory, and each.rsfile represents a separate crate. Integration tests can only test the library's public API (interfaces marked withpub) and cannot access private functions. This simulates the scenario of "external users using your library."cargo test --test filenameallows you to run only specific integration test files.
▶ Example 4: Benchmarking Concepts (Difficulty ⭐⭐⭐)
Output:
<avg.as_micros()>: Average <avg.as_nanos() % 1_000>.<name> microsecond (<iterations> Next iteration)
=== Benchmark Demo: Performance Comparison of Sorting Algorithms ===
Data Volume: <size> element
=== Conclusion ===
Bubble Sort O(n^2) It slows down noticeably when dealing with large amounts of data
Quick Sort O(n log n) Significant performance advantages with large data sets
Rust nightly Version Usage `cargo bench` Run a benchmark test
We recommend using the stable version `criterion` crate Conduct a benchmark test
// ============================================
// Benchmarking (Benchmark) Concept Demo
// Using #[bench] and Bencher (Requires nightly Rust)
// Note: Rust Stable Version uses the criterion crate for benchmark testing
// Here, we'll use a"Manual Timing"A method for simulating benchmark testing concepts
// ============================================
use std::time::Instant;
// ============================================
// Comparing the Performance of Two Sorting Algorithms
// ============================================
/// Bubble Sort (O(n^2) - Slow)
fn bubble_sort(arr: &mut [i32]) {
let n = arr.len();
for i in 0..n {
for j in 0..n - 1 - i {
if arr[j] > arr[j + 1] {
arr.swap(j, j + 1);
}
}
}
}
/// Quick Sort (O(n log n) - Fast)
fn quick_sort(arr: &mut [i32]) {
if arr.len() <= 1 {
return;
}
let pivot = partition(arr);
quick_sort(&mut arr[..pivot]);
quick_sort(&mut arr[pivot + 1..]);
}
fn partition(arr: &mut [i32]) -> usize {
let len = arr.len();
let pivot = arr[len - 1];
let mut i = 0;
for j in 0..len - 1 {
if arr[j] <= pivot {
arr.swap(i, j);
i += 1;
}
}
arr.swap(i, len - 1);
i
}
/// Manual Benchmarking Functions
fn bench_sort<F>(name: &str, mut sort_fn: F, data: &[i32], iterations: u32)
where
F: FnMut(&mut [i32]),
{
let mut total_duration = std::time::Duration::new(0, 0);
for _ in 0..iterations {
let mut cloned = data.to_vec();
let start = Instant::now();
sort_fn(&mut cloned);
total_duration += start.elapsed();
}
let avg = total_duration / iterations;
println!(" {}: Average {}.{:03} microsecond ({} Next iteration)",
name,
avg.as_micros(),
avg.as_nanos() % 1_000,
iterations);
}
fn main() {
println!("=== Benchmark Demo: Performance Comparison of Sorting Algorithms ===\n");
// Generate Random Data
let data_sizes = [100, 500, 1000];
for &size in &data_sizes {
// Generate a random array
let data: Vec<i32> = (0..size).map(|i| {
// Simulating Random Numbers Using a Simple Linear Congruential Generator
((i * 1234567 + 987654) % 100000) as i32
}).collect();
println!("Data Volume: {} element", size);
let iterations = if size <= 100 { 100 } else { 10 };
bench_sort("Bubble Sort", |arr| bubble_sort(arr), &data, iterations);
bench_sort("Quick Sort", |arr| quick_sort(arr), &data, iterations);
println!();
}
println!("=== Conclusion ===");
println!("Bubble Sort O(n^2) It slows down noticeably when dealing with large amounts of data");
println!("Quick Sort O(n log n) Significant performance advantages with large data sets");
println!();
println!("Rust nightly Version Usage `cargo bench` Run a benchmark test");
println!("We recommend using the stable version `criterion` crate Conduct a benchmark test");
}
// ============================================
// Simulated nightly Rust Benchmarking (For reference only)
// Requires #![feature(test)] and extern crate test;
// ============================================
// The following code is available in nightly Rust:
//
// #![cfg(test)]
// #![feature(test)]
// extern crate test;
//
// #[cfg(test)]
// mod bench_tests {
// use super::*;
// use test::Bencher;
//
// #[bench]
// fn bench_bubble_sort_100(b: &mut Bencher) {
// let data = vec![5, 3, 1, 4, 2, 7, 6, 9, 8, 0];
// b.iter(|| {
// let mut arr = data.clone();
// bubble_sort(&mut arr);
// });
// }
//
// #[bench]
// fn bench_quick_sort_100(b: &mut Bencher) {
// let data = vec![5, 3, 1, 4, 2, 7, 6, 9, 8, 0];
// b.iter(|| {
// let mut arr = data.clone();
// quick_sort(&mut arr);
// });
// }
// }
Output:
=== Benchmark Demo: Performance Comparison of Sorting Algorithms ===
Data Volume: 100 element
Bubble Sort: Average 45.123 microsecond (100 Next iteration)
Quick Sort: Average 3.456 microsecond (100 Next iteration)
Data Volume: 500 element
Bubble Sort: Average 1023.567 microsecond (10 Next iteration)
Quick Sort: Average 18.234 microsecond (10 Next iteration)
Data Volume: 1000 element
Bubble Sort: Average 4089.890 microsecond (10 Next iteration)
Quick Sort: Average 39.012 microsecond (10 Next iteration)
=== Conclusion ===
Bubble Sort O(n^2) It slows down noticeably when dealing with large amounts of data
Quick Sort O(n log n) Significant performance advantages with large data sets
Rust nightly Version Usage `cargo bench` Run a benchmark test
We recommend using the stable version `criterion` crate Conduct a benchmark test
Benchmarking measures code execution time to ensure that performance does not degrade during refactoring. Rust Nightly includes the
#[bench]attribute by default, while the stable release recommends using thecriterioncrate. Note: Benchmarking isn't about "faster is better"—the key is to establish a performance baseline to ensure there are no unexpected performance regressions when modifying the code. The example above demonstrates the concept of benchmarking by manually timingInstant::now().
▶ Example 5: Comprehensive Exercise—A Test-Driven String Library (Difficulty ⭐⭐⭐)
Output:
is_palindrome('racecar'): <is_palindrome("racecar")>
is_palindrome('hello'): <is_palindrome("hello")>
|<line>|
// ============================================
// Comprehensive Example: TDD-style String Library
// ============================================
pub fn is_palindrome(s: &str) -> bool {
let clean: String = s.chars().filter(|c| c.is_alphanumeric()).map(|c| c.to_lowercase().next().unwrap()).collect();
let reversed: String = clean.chars().rev().collect();
clean == reversed
}
pub fn word_wrap(text: &str, width: usize) -> Vec<String> {
let mut lines = Vec::new();
let mut current = String::new();
for word in text.split_whitespace() {
if current.len() + word.len() + 1 > width && !current.is_empty() {
lines.push(current.trim().to_string());
current.clear();
}
if !current.is_empty() { current.push(' '); }
current.push_str(word);
}
if !current.is_empty() { lines.push(current); }
lines
}
pub fn count_words(text: &str) -> std::collections::HashMap<String, u32> {
let mut freq = std::collections::HashMap::new();
for word in text.split_whitespace() {
let clean: String = word.chars().filter(|c| c.is_alphabetic()).map(|c| c.to_lowercase().next().unwrap()).collect();
if !clean.is_empty() { *freq.entry(clean).or_insert(0) += 1; }
}
freq
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_palindrome_simple() {
assert!(is_palindrome("racecar"));
assert!(is_palindrome("A man a plan a canal Panama"));
assert!(!is_palindrome("hello"));
}
#[test]
fn test_palindrome_empty() {
assert!(is_palindrome(""));
assert!(is_palindrome("a"));
}
#[test]
fn test_word_wrap() {
let result = word_wrap("The quick brown fox jumps", 10);
assert_eq!(result, vec!["The quick", "brown fox", "jumps"]);
}
#[test]
fn test_word_wrap_short() {
let result = word_wrap("Hello", 10);
assert_eq!(result, vec!["Hello"]);
}
#[test]
fn test_count_words() {
let freq = count_words("the cat and the dog");
assert_eq!(freq.get("the"), Some(&2));
assert_eq!(freq.get("cat"), Some(&1));
assert_eq!(freq.get("dog"), Some(&1));
}
}
fn main() {
println!("is_palindrome('racecar'): {}", is_palindrome("racecar"));
println!("is_palindrome('hello'): {}", is_palindrome("hello"));
let wrapped = word_wrap("The quick brown fox jumps over the lazy dog", 15);
for line in &wrapped { println!("|{:<15}|", line); }
let freq = count_words("the cat sat on the mat and the cat");
println!("\nWord frequency: {:?}", freq);
}
Output:
is_palindrome('racecar'): true
is_palindrome('hello'): false
|The quick brown|
|fox jumps over |
|the lazy dog |
Word frequency: {"the": 3, "cat": 2, "sat": 1, "on": 1, "mat": 1, "and": 1}
TDD Process: Write tests first (
test_palindrome_simple, etc.) → Then write the implementation → Runcargo test.#[cfg(test)]Ensure that the test module is compiled only during testing.use super::*Import the public API of the module under test. This example demonstrates a combination of unit testing and actual execution.
❓ FAQ
#[test] and #[cfg(test)]?#[test] marks a function as a test function, while #[cfg(test)] wraps a module so that it compiles only in test mode.tests/ directory?tests/ directory is treated as an independent crate and can only access your library's public API.📖 Summary
#[test]marks a test function;cargo testruns all tests;assert!/assert_eq!/assert_ne!are the three major assertion macros#[should_panic]Test expected panic scenarios;#[ignore]Skip tests that do not need to be run at this time#[cfg(test)]Conditional compilation of test modules to ensure that test code does not appear in production builds///Documentation comments use Markdown format; code blocks are automatically converted to doc-tests;cargo doc --opengenerates HTML documentation- Integration Tests are located in the
tests/directory; each file compiles independently and can only test the public API (black-box perspective). - Benchmarking: To measure code performance, use
#[bench]for the nightly build; for the stable release, we recommend thecriterioncrate.
📝 Exercises
-
Difficulty ⭐: Write a program that includes the
is_prime(n: u32) -> boolfunction to determine whether a number is prime. Write at least 5 test cases for this function (including the boundary cases: 0, 1, 2, prime numbers, and composite numbers). Useassert!,assert_eq!, and#[should_panic](which causes a panic if the parameter is 0) at least once each. -
Difficulty ⭐⭐: Create a "Temperature Converter" library containing two functions:
celsius_to_fahrenheit(c: f64) -> f64andfahrenheit_to_celsius(f: f64) -> f64. Write complete documentation comments for these two functions (including examples, parameter descriptions, and return value descriptions), and ensure that the unit tests pass. Also, write unit tests to verify boundary values (such as 0°C = 32°F, 100°C = 212°F, and -40°C = -40°F). -
Difficulty ⭐⭐⭐: Implement a "Simple Calculator" library that supports five operations:
add,subtract,multiply,divide, andpower(power operations). Create a comprehensive testing framework for this library: (1) Unit tests must cover all functions, including edge cases (division by zero, 0th power, etc.); (2) Simulate the creation of atests/calculator_integration_test.rsintegration test file (write the complete content in the comments) to test "consecutive operations" scenarios (such asadd(2,3) → multiply(5,4) → power(20,2)). Use#[should_panic]to test the division-by-zero scenario.