Rust: اختبار لغة «Rust» وتوثيقها

آخر تحديث: 2026-08-26

الاختبار هو «مفتش الجودة» للكود — فليكتشف الاختبار الأخطاء قبل أن يكتشفها المستخدمون. أما التوثيق فهو «دليل الاستخدام» للكود — فهو لا يكتفي بإرشاد الآخرين إلى كيفية استخدامه فحسب، بل يذكرك أنت أيضًا في المستقبل بالسبب الذي دفعك إلى كتابته بهذه الطريقة.

إذا كان كتابة الكود أشبه بـ«بناء منزل»، فإن الاختبار هو «تقرير فحص الجودة»، والتوثيق هو «دليل المستخدم». فهل تجرؤ على الانتقال إلى منزل لم يخضع للاختبار؟ المفهوم الأساسي لـ TDD (التطوير القائم على الاختبار) بسيط: اكتب الاختبارات أولاً، ثم الكود — تمامًا مثل وضع معايير الجودة قبل تصنيع المنتج.


1. ما ستتعلمه



2. قصة أحد مفتشي الجودة

(1) الصراع: المصانع التي تفتقر إلى مراقبة الجودة

يعمل بوب كمفتش لمراقبة الجودة في مصنع للسيارات. وبعد فترة وجيزة من بدء عمله، اكتشف حقيقة مروعة:

«لو خضعت كل سيارة للاختبار قبل خروجها من المصنع، لما حصل العملاء في النهاية على عجلات قيادة مربعة الشكل...»

(2) مناهج الاختبار والتوثيق في لغة Rust

توفر لغة «Rust» مجموعة كاملة من أدوات الاختبار والتوثيق، تمامًا مثل نظام إدارة الجودة في مصنع يُدار بشكل جيد:

TEXT 📖 للعرض فقط
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);
}

يشبه إطار عمل الاختبار في لغة Rust خط تجميع لمراقبة الجودة: #[test] يحدد كل «محطة لمراقبة الجودة»، وcargo test يبدأ تشغيل خط التجميع بأكمله، وassert_eq! يمثل معايير مراقبة الجودة. تشبه تعليقات التوثيق /// «التعليمات» المرفقة بكل جزء، بينما يجمع cargo doc جميع التعليمات في كتيب واحد.



3. المفاهيم الأساسية

(1) إطار عمل الاختبار والتوثيق

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) مقارنة بين أنواع الاختبارات

نوع الاختبار الكلمة المفتاحية/الموقع نطاق الاختبار القياس السيناريوهات القابلة للتطبيق
اختبار الوحدة #[test] + #[cfg(test)] الوظائف أو الوحدات النمطية الفردية فحص كل مكون التحقق من منطق الوظيفة
اختبار الوثائق كتل الكود في /// أمثلة كود واجهة برمجة التطبيقات (API) الأمثلة الواردة في الدليل التأكد من أن الأمثلة الواردة في الوثائق تعمل بشكل صحيح
اختبار التكامل الملف .rs الموجود في الدليل tests/ السلوك العام لواجهات برمجة التطبيقات الخارجية اختبار المركبة على الطريق التحقق من التعاون بين الوحدات
معيار الأداء #[bench] / cargo bench مقاييس الأداء اختبار السرعة كود حساس للأداء

(3) مقارنة ماكروات التأكيد

الماكرو الغرض شروط النجاح رسالة الفشل
assert!(expr) الشروط المنطقية expr == true assertion failed: expr
assert_eq!(a, b) التكافؤ a == b assertion failed: (left == right)
assert_ne!(a, b) عدم التماثل a != b assertion failed: (left != right)


4. أمثلة على الاختبار والتوثيق

(1) ▶ المثال:الاختبارات الوحدوية ووحدات ماكرو التأكيد (مستوى الصعوبة ⭐)

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);
    }
}

الناتج:

TEXT 📖 للعرض فقط
=== 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

نتيجة الاختبار (cargo test):

TEXT 📖 للعرض فقط
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

وحدة الاختبار محاطة بعلامة #[cfg(test)]، والتي تعني «التجميع في وضع الاختبار فقط» — ولن يظهر هذا الكود على الإطلاق في إصدارات الإنتاج (cargo build --release). يستورد use super::* جميع الدوال من الوحدة النمطية الأم (أي الكود الرئيسي في الملف الحالي). يتحقق #[should_panic(expected = "...")] من أن الدالة ستُطلق حالة الذعر بشكل صحيح في ظل ظروف محددة.


(2) ▶ المثال:التعليقات التوضيحية على المستندات ووثيقة «cargo doc» (مستوى الصعوبة: ⭐⭐)

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

الناتج:

TEXT 📖 للعرض فقط
=== 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)

/// تستخدم تعليقات التوثيق تنسيق Markdown وتدعم أقسامًا مثل # Examples و# Parameters و# Return Value. تتحول أمثلة الكود الموجودة في كتل الكود تلقائيًّا إلى «اختبارات توثيقية» — حيث يقوم cargo test بترجمة هذه الكتل وتشغيلها للتأكد من أن الأمثلة الواردة في التوثيق تعمل دائمًا بشكل صحيح. cargo doc --open يُنشئ وثائق HTML جميلة ويفتحها في متصفح.


(3) ▶ المثال:اختبار التكامل وتنظيم الاختبارات (مستوى الصعوبة ⭐⭐)

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

الناتج:

TEXT 📖 للعرض فقط
=== 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

توجد اختبارات التكامل في المجلد tests/ داخل الدليل الجذري للمشروع، ويمثل كل ملف .rs مكتبة منفصلة. لا يمكن لاختبارات التكامل إلا اختبار واجهة برمجة التطبيقات (API) العامة للمكتبة (الواجهات المُشار إليها بـ pub) ولا يمكنها الوصول إلى الدوال الخاصة. وهذا يحاكي سيناريو «استخدام المستخدمين الخارجيين لمكتبتك». يتيح لك cargo test --test filename تشغيل ملفات اختبار التكامل المحددة فقط.


(4) ▶ المثال:مفاهيم المقارنة المعيارية (مستوى الصعوبة ⭐⭐⭐)

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

الناتج:

TEXT 📖 للعرض فقط
=== 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

تقيس عملية قياس الأداء وقت تنفيذ الكود لضمان عدم تدهور الأداء أثناء إعادة هيكلة الكود. يتضمن إصدار Rust Nightly السمة #[bench] بشكل افتراضي، بينما يوصي الإصدار المستقر باستخدام الكريت criterion. ملاحظة: لا تعني المقارنة المعيارية أن «الأسرع هو الأفضل» — فالأمر الأساسي هو تحديد خط أساس للأداء لضمان عدم حدوث أي تراجع غير متوقع في الأداء عند تعديل الكود. يوضح المثال أعلاه مفهوم المقارنة المعيارية من خلال قياس وقت Instant::now() يدويًّا.


(5) ▶ المثال:تمرين شامل — مكتبة سلاسل نصية تعتمد على الاختبارات (مستوى الصعوبة ⭐⭐⭐)

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);
}

الناتج:

TEXT 📖 للعرض فقط
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: اكتب الاختبارات أولاً (test_palindrome_simple، إلخ) → ثم اكتب التنفيذ → قم بتشغيل cargo test. #[cfg(test)] تأكد من أن وحدة الاختبار لا يتم ترجمتها إلا أثناء الاختبار. use super::* قم باستيراد واجهة برمجة التطبيقات (API) العامة للوحدة قيد الاختبار. يوضح هذا المثال الجمع بين الاختبار الوحدوي والتنفيذ الفعلي.



❓ أسئلة شائعة

س: ما الفرق بين #[test] و#[cfg(test)]؟ ج: #[test] يُشير إلى دالة ما على أنها دالة اختبارية، بينما #[cfg(test)] يلف الوحدة النمطية بحيث يتم ترجمتها في وضع الاختبار فقط. #[test] تعني «هذه الدالة هي دالة اختبار»، و#[cfg(test)] تعني «هذا الكود مخصص للاختبار فقط». وعادةً ما تُستخدم معًا: #[cfg(test)] mod tests { ... } تُغلف الوحدة النمطية الاختبارية بأكملها، وتُعلم كل دالة اختبار داخل الوحدة النمطية بـ #[test].

س: ما الفرق بين اختبارات الوثائق (doc-tests) واختبارات الوحدة (unit tests)؟ ج: تختبر اختبارات الوثائق (doc-tests) صحة تشغيل أمثلة الكود الواردة في الوثائق، بينما تختبر اختبارات الوحدة (unit tests) صحة منطق الدالة. تُكتب أكواد اختبارات الوثائق داخل كتل أكواد مُعلَّمة بـ ///، ويقوم cargo test بتجميعها وتنفيذها تلقائيًا. وهذا يضمن أن تكون الأمثلة الواردة في الوثائق محدَّثة دائمًا — فإذا تغير الكود دون تحديث الوثائق، فستفشل اختبارات الوثائق.

س: لماذا توضع اختبارات التكامل في الدليل tests/؟ ج: لأن كل ملف في الدليل tests/ يُعامل كـ crate مستقل ولا يمكنه الوصول إلا إلى واجهة برمجة التطبيقات (API) العامة لمكتبتك. وهذا يحاكي السيناريو الذي «يستخدم فيه المستخدمون الخارجيون مكتبتك». أما اختبارات الوحدة، فهي موضوعة في الوحدة النمطية #[cfg(test)] داخل ملفات الكود المصدري ويمكنها الوصول إلى الدوال الخاصة. ويخدم كل منهما غرضًا مختلفًا: اختبارات الوحدة هي اختبارات الصندوق الأبيض (تفحص التنفيذ الداخلي)، بينما اختبارات التكامل هي اختبارات الصندوق الأسود (تفحص السلوك الخارجي فقط).

س: ما هي المعلمات الشائعة لـ cargo test؟ ج: يتميز cargo test بمرونة كبيرة: cargo test name لا يُشغّل سوى الاختبارات ذات الأسماء المطابقة، وcargo test -- --nocapture يعرض مخرجات println! في الاختبارات، وcargo test -- --test-threads=1 يعمل في خيط واحد (مناسب للاختبارات ذات الحالة المشتركة)، وcargo test --test filename لا يُشغّل سوى ملفات اختبار التكامل المحددة، وcargo test -- --ignored لا يُشغّل سوى الاختبارات المُعلَّمة بـ #[ignore].

س: هل اختبار الأداء ضروري؟ متى تكون هناك حاجة إليه؟ ج: يُعد اختبار الأداء أمرًا ضروريًا عندما يكون الكود الخاص بك جزءًا من مسار حرج من حيث الأداء (مثل التداول عالي التردد، أو محركات الألعاب، أو مسارات معالجة البيانات). ولا تكمن قيمته في قياس «مدى سرعة تشغيله»، بل في تحديد خط أساس للأداء — بحيث يمكنك، عند تعديل الكود، اكتشاف أي تدهور في الأداء على الفور. بالنسبة لمعظم أكواد الأعمال، اكتب اختبارات الوحدة المناسبة أولاً، ولا تلجأ إلى إجراء اختبارات الأداء إلا عندما يصبح الأداء عائقًا.


📖 ملخص


📝 تمارين

  1. الصعوبة ⭐: اكتب برنامجًا يتضمن الدالة is_prime(n: u32) -> bool لتحديد ما إذا كان عدد ما عددًا أوليًا أم لا. اكتب ما لا يقل عن 5 حالات اختبار لهذه الدالة (بما في ذلك الحالات الحدية: 0، 1، 2، الأعداد الأولية، والأعداد المركبة). استخدم assert! وassert_eq! و#[should_panic] (التي تثير حالة ذعر إذا كانت القيمة 0) مرة واحدة على الأقل لكل منها.

  2. الصعوبة ⭐⭐: أنشئ مكتبة «محول درجات الحرارة» تحتوي على دالتين: celsius_to_fahrenheit(c: f64) -> f64 وfahrenheit_to_celsius(f: f64) -> f64. اكتب تعليقات توثيقية كاملة لهاتين الدالتين (بما في ذلك الأمثلة، ووصف المعلمات، ووصف قيم الإرجاع)، وتأكد من اجتياز اختبارات الوحدة. كما يجب كتابة اختبارات الوحدة للتحقق من القيم الحدية (مثل 0 درجة مئوية = 32 درجة فهرنهايت، و100 درجة مئوية = 212 درجة فهرنهايت، و-40 درجة مئوية = -40 درجة فهرنهايت).

  3. الصعوبة ⭐⭐⭐: قم بتنفيذ مكتبة «آلة حاسبة بسيطة» تدعم خمس عمليات: add، subtract، multiply، divide، وpower (عمليات القوة). أنشئ إطار عمل اختبار شامل لهذه المكتبة: (1) يجب أن تغطي الاختبارات الوحدوية جميع الدوال، بما في ذلك الحالات الحدية (القسمة على الصفر، القوة الصفرية، إلخ)؛ (2) قم بمحاكاة إنشاء ملف اختبار تكاملي tests/calculator_integration_test.rs (اكتب المحتوى الكامل في التعليقات) لاختبار سيناريوهات «العمليات المتتالية» (مثل add(2,3) → multiply(5,4) → power(20,2)). استخدم #[should_panic] لاختبار سيناريو القسمة على الصفر.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%