পাঠ ১২.৪

TDD দিয়ে functionality যোগ করা

Adding Functionality with Test Driven Development

এখন আসল search logic add করব — Test Driven Development (TDD) approach-এ।

TDD-এর তিন step

  1. একটা test লেখো যেটা fail করে; verify কর কেন fail করছে।
  2. Test pass করার মতো ন্যূনতম code লেখো।
  3. Refactor কর; test পাশ থাকবে।
  4. Repeat।

এতে test coverage সবসময় high থাকে; bugs প্রথমেই ধরা পড়ে।

Failing test লেখা

src/lib.rsrust
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    unimplemented!();
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn one_result() {
        let query = "duct";
        let contents = "\
Rust:
safe, fast, productive.
Pick three.";

        assert_eq!(vec!["safe, fast, productive."], search(query, contents));
    }
}

"\ string literal-এর শুরুতে — first newline ignore করতে।

Lifetime কেন

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str>

Return-করা &str slice-গুলো contents-এর substring। তাই return value-র lifetime contents-এর সাথে tied — query-এর সাথে না (query function-শেষে drop হলেও result valid থাকবে)। এই relationship lifetime annotation দিয়ে স্পষ্ট।

এই lifetime না দিলে compile error:

compile errortext
error[E0106]: missing lifetime specifier
 --> src/lib.rs:1:51
  |
1 | pub fn search(query: &str, contents: &str) -> Vec<&str> {
  |                      ----            ----         ^ expected named lifetime parameter

Pass করানো — ন্যূনতম code

Step 1 — empty Vec

প্রথমে compile করানো (unimplemented! sustitute):

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    vec![]
}

Test compile হবে কিন্তু fail করবে — empty vec ≠ expected।

Step 2 — line দিয়ে iterate

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    for line in contents.lines() {
        // do something with line
    }
}

.lines() string-এর প্রতিটা line-এর iterator দেয়।

Step 3 — query check

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    for line in contents.lines() {
        if line.contains(query) {
            // do something with line
        }
    }
}

.contains() string method — substring check।

Step 4 — push এবং return

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let mut results = Vec::new();

    for line in contents.lines() {
        if line.contains(query) {
            results.push(line);
        }
    }

    results
}

Test pass:

cargo testtext
running 1 test
test tests::one_result ... ok

main-এ search use করা

run function update — search-এর result iterate করে print:

src/lib.rsrust
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let contents = fs::read_to_string(config.file_path)?;

    for line in search(&config.query, &contents) {
        println!("{line}");
    }

    Ok(())
}
terminalbash
$ cargo run -- frog poem.txt
How public, like a frog

$ cargo run -- body poem.txt
I'm nobody! Who are you?
Are you nobody, too?
How dreary to be somebody!

$ cargo run -- monomorphization poem.txt
(কোনো output নেই — match পাইনি)

এই পাঠ থেকে যা শিখলে

  • TDD — fail-test → minimal pass code → refactor। Coverage সবসময় high।
  • fn search<'a>(...) -> Vec<&'a str> — return-এর reference contents থেকে আসছে, তাই lifetime tied।
  • .lines(), .contains() — Rust string-এর ব্যবহারিক method।