Rust: Testes e documentação em Rust

Última atualização: 2026-08-26

Os testes são o “inspetor de qualidade” do código — deixe que os testes encontrem os bugs antes que os usuários o façam. A documentação é o “manual de instruções” do código — ela não apenas explica aos outros como usá-lo, mas também lembra ao seu “eu futuro” por que você o escreveu dessa maneira.

Se escrever código é como “construir uma casa”, então os testes são o “relatório de inspeção de qualidade” e a documentação é o “manual do usuário”. Você se atreveria a morar em uma casa que não foi testada? O conceito central do TDD (Desenvolvimento Orientado a Testes) é simples: escreva os testes primeiro, depois o código — assim como se define padrões de qualidade antes de fabricar um produto.


1. O que você vai aprender


2. A história de um inspetor de qualidade

(1) A luta: fábricas sem controle de qualidade

Bob é inspetor de controle de qualidade em uma fábrica de automóveis. Pouco depois de começar a trabalhar, ele descobriu um fato chocante:

“Se todos os carros fossem testados antes de saírem da fábrica, os clientes não acabariam com volantes quadrados...”

(2) Abordagens para testes e documentação em Rust

O Rust oferece um conjunto completo de ferramentas de teste e documentação, muito semelhante ao sistema de gestão da qualidade de uma fábrica bem administrada:

TEXT 📖 Somente leitura
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
RUST
/// 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);
}

A estrutura de testes do Rust é como uma linha de montagem de controle de qualidade: #[test] marca cada “estação de controle de qualidade”, cargo test dá início a toda a linha de montagem e assert_eq! representa os padrões de controle de qualidade. Os comentários de documentação /// são como as “instruções” anexadas a cada peça, e cargo doc reúne todas as instruções em um único manual.


3. Conceitos fundamentais

(1) Estrutura de testes e documentação

100%
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) Comparação entre tipos de testes

Tipo de teste Palavra-chave/Localização Escopo do teste Analogia Cenários aplicáveis
Testes unitários #[test] + #[cfg(test)] Funções ou módulos individuais Verificar cada componente Verificar a lógica da função
Teste de documentação Blocos de código em /// Código de exemplo da API Exemplos incluídos no manual Verificar se os exemplos na documentação estão funcionando
Testes de integração O arquivo .rs no diretório tests/ Comportamento geral das APIs externas Testes de estrada do veículo Verificação da colaboração entre módulos
Teste de desempenho #[bench] / cargo bench Métricas de desempenho Teste de velocidade Código sensível ao desempenho

(3) Comparação de macros de asserção

Macro Finalidade Condições de sucesso Mensagem de erro
assert!(expr) Condições booleanas expr == true assertion failed: expr
assert_eq!(a, b) Equivalência a == b assertion failed: (left == right)
assert_ne!(a, b) Assimetria a != b assertion failed: (left != right)

4. Exemplos de testes e documentação

▶ Exemplo 1: Testes unitários e macros de asserção (Dificuldade ⭐)

RUST
// ============================================
// 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);
    }
}

Resultado:

TEXT 📖 Somente leitura
=== 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

Saída do teste (cargo test):

TEXT 📖 Somente leitura
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

O módulo de teste está envolto em #[cfg(test)], o que significa “compilar apenas no modo de teste” — esse código não estará presente de forma alguma nas compilações de produção (cargo build --release). use super::* importa todas as funções do módulo pai (ou seja, o código principal no arquivo atual). #[should_panic(expected = "...")] verifica se uma função irá acionar corretamente um panic sob condições específicas.


▶ Exemplo 2: Anotações em documentos e o cargo doc (Dificuldade: ⭐⭐)

RUST
// ============================================
// 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(""));
    }
}

Resultado:

TEXT 📖 Somente leitura
=== 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)

/// Os comentários da documentação utilizam o formato Markdown e suportam seções como # Examples, # Parameters e # Return Value. O código de exemplo nos blocos de código se transforma automaticamente em “doc-tests” — cargo test compila e executa esses blocos de código para garantir que os exemplos na documentação estejam sempre funcionais. cargo doc --open gera uma bela documentação em HTML e a abre em um navegador.


▶ Exemplo 3: Testes de integração e organização dos testes (Dificuldade ⭐⭐)

RUST
// ============================================
// 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);
// }

Resultado:

TEXT 📖 Somente leitura
=== 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

Os testes de integração estão localizados na pasta tests/, no diretório raiz do projeto, e cada arquivo .rs representa um crate separado. Os testes de integração podem testar apenas a API pública da biblioteca (interfaces marcadas com pub) e não podem acessar funções privadas. Isso simula o cenário de “usuários externos utilizando sua biblioteca”. cargo test --test filename permite que você execute apenas arquivos específicos de testes de integração.


▶ Exemplo 4: Conceitos de benchmarking (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// 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);
//         });
//     }
// }

Resultado:

TEXT 📖 Somente leitura
=== 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

O benchmark mede o tempo de execução do código para garantir que o desempenho não seja prejudicado durante a refatoração. O Rust Nightly inclui o atributo #[bench] por padrão, enquanto a versão estável recomenda o uso do crate criterion. Observação: o benchmarking não significa que “quanto mais rápido, melhor” — o importante é estabelecer uma linha de base de desempenho para garantir que não haja regressões inesperadas de desempenho ao modificar o código. O exemplo acima demonstra o conceito de benchmarking ao cronometrar manualmente o Instant::now().


▶ Exemplo 5: Exercício abrangente — Uma biblioteca de strings orientada a testes (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// 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);
}

Resultado:

TEXT 📖 Somente leitura
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}

Processo TDD: Escreva os testes primeiro (test_palindrome_simple, etc.) → Em seguida, escreva a implementação → Execute cargo test. #[cfg(test)] Certifique-se de que o módulo de teste seja compilado apenas durante os testes. use super::* Importe a API pública do módulo em teste. Este exemplo demonstra uma combinação de testes unitários e execução real.


❓ Perguntas Frequentes

P: Qual é a diferença entre #[test] e #[cfg(test)]? R: #[test] marca uma função como função de teste, enquanto #[cfg(test)] envolve um módulo para que ele seja compilado apenas no modo de teste. #[test] significa “esta função é um teste”, e #[cfg(test)] significa “este código é apenas para testes”. Eles geralmente são usados em combinação: #[cfg(test)] mod tests { ... } envolve todo o módulo de teste, e cada função de teste dentro do módulo é marcada com #[test].

P: Qual é a diferença entre doc-tests e testes de unidade? R: Os doc-tests verificam se o código de exemplo na documentação é executado corretamente, enquanto os testes de unidade verificam se a lógica da função está correta. O código dos doc-tests é escrito dentro de blocos de código anotados com ///, e o cargo test os compila e executa automaticamente. Isso garante que os exemplos na documentação estejam sempre atualizados — se o código for alterado, mas a documentação não for atualizada, os doc-tests falharão.

P: Por que os testes de integração são colocados no diretório tests/? R: Porque cada arquivo no diretório tests/ é tratado como um crate independente e só pode acessar a API pública da sua biblioteca. Isso simula o cenário em que “usuários externos utilizam sua biblioteca”. Os testes unitários, por outro lado, são colocados no módulo #[cfg(test)] dentro dos arquivos de código-fonte e podem acessar funções privadas. Os dois têm finalidades diferentes: os testes unitários são testes de caixa branca (que examinam a implementação interna), enquanto os testes de integração são testes de caixa preta (que examinam apenas o comportamento externo).

P: Quais são os parâmetros comuns para cargo test? R: cargo test é muito flexível: cargo test name executa apenas testes com nomes correspondentes, cargo test -- --nocapture exibe a saída do println! nos testes, cargo test -- --test-threads=1 é executado em uma única thread (adequado para testes com estado compartilhado), cargo test --test filename executa apenas arquivos específicos de testes de integração e cargo test -- --ignored executa apenas testes marcados com #[ignore].

P: O benchmarking é necessário? Quando ele é necessário? R: O benchmarking é essencial quando seu código faz parte de um caminho crítico para o desempenho (como negociações de alta frequência, motores de jogos ou pipelines de processamento de dados). Seu valor não está em medir “a rapidez com que ele é executado”, mas em estabelecer uma linha de base de desempenho — para que, ao modificar o código, você possa detectar imediatamente qualquer degradação no desempenho. Para a maioria dos códigos empresariais, escreva primeiro testes de unidade adequados e recorra ao benchmarking somente quando o desempenho se tornar um gargalo.


📖 Resumo


📝 Exercícios

  1. Dificuldade ⭐: Escreva um programa que inclua a função is_prime(n: u32) -> bool para determinar se um número é primo. Escreva pelo menos 5 casos de teste para essa função (incluindo os casos-limite: 0, 1, 2, números primos e números compostos). Use assert!, assert_eq! e #[should_panic] (que gera um erro de pânico se o parâmetro for 0) pelo menos uma vez cada.

  2. Dificuldade ⭐⭐: Crie uma biblioteca chamada “Conversor de Temperatura” contendo duas funções: celsius_to_fahrenheit(c: f64) -> f64 e fahrenheit_to_celsius(f: f64) -> f64. Escreva comentários de documentação completos para essas duas funções (incluindo exemplos, descrições dos parâmetros e descrições dos valores de retorno) e certifique-se de que os testes unitários sejam aprovados. Além disso, escreva testes unitários para verificar valores limite (como 0 °C = 32 °F, 100 °C = 212 °F e -40 °C = -40 °F).

  3. Dificuldade ⭐⭐⭐: Implemente uma biblioteca “Calculadora Simples” que suporte cinco operações: add, subtract, multiply, divide e power (operações de potência). Crie uma estrutura de testes abrangente para essa biblioteca: (1) Os testes unitários devem abranger todas as funções, incluindo casos extremos (divisão por zero, potência zero, etc.); (2) Simule a criação de um arquivo de teste de integração tests/calculator_integration_test.rs (escreva o conteúdo completo nos comentários) para testar cenários de “operações consecutivas” (como add(2,3) → multiply(5,4) → power(20,2)). Use #[should_panic] para testar o cenário de divisão por zero.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%