🦀 Functional Rust
🎬 How Rust Iterators Work Lazy evaluation, chaining, collect(), and zero-cost abstractions.
📝 Text version (for readers / accessibility)

• Iterators are lazy — .map(), .filter(), .take() build a chain but do no work until consumed

• .collect() triggers evaluation, transforming the chain into a Vec, HashMap, or other collection

• Zero-cost abstraction: iterator chains compile to the same machine code as hand-written loops

• .iter() borrows, .into_iter() consumes, .iter_mut() borrows mutably

• Chaining replaces nested loops with a readable, composable pipeline

089: Bob — String Pattern Matching

Difficulty: Beginner Category: String Processing Concept: Multiple string conditions combined with tuple pattern matching Key Insight: Matching on a tuple of booleans (silence, yelling, question) is elegant in both languages and avoids nested if-else chains.
/// Bob — String Pattern Matching
///
/// Ownership: Input is borrowed &str. Responses are &'static str (no allocation).

fn is_question(s: &str) -> bool {
    s.trim().ends_with('?')
}

fn is_yelling(s: &str) -> bool {
    let has_letter = s.chars().any(|c| c.is_alphabetic());
    has_letter && s == s.to_uppercase()
}

fn is_silence(s: &str) -> bool {
    s.trim().is_empty()
}

pub fn response_for(s: &str) -> &'static str {
    match (is_silence(s), is_yelling(s), is_question(s)) {
        (true, _, _) => "Fine. Be that way!",
        (_, true, true) => "Calm down, I know what I'm doing!",
        (_, true, false) => "Whoa, chill out!",
        (_, false, true) => "Sure.",
        _ => "Whatever.",
    }
}

/// Version 2: Using if-else chain (more readable for some)
pub fn response_for_v2(s: &str) -> &'static str {
    let trimmed = s.trim();
    if trimmed.is_empty() {
        "Fine. Be that way!"
    } else if is_yelling(s) && is_question(s) {
        "Calm down, I know what I'm doing!"
    } else if is_yelling(s) {
        "Whoa, chill out!"
    } else if is_question(s) {
        "Sure."
    } else {
        "Whatever."
    }
}

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

    #[test]
    fn test_yelling() {
        assert_eq!(response_for("WATCH OUT!"), "Whoa, chill out!");
    }

    #[test]
    fn test_question() {
        assert_eq!(response_for("Does this work?"), "Sure.");
    }

    #[test]
    fn test_yelling_question() {
        assert_eq!(response_for("WHAT IS THIS?"), "Calm down, I know what I'm doing!");
    }

    #[test]
    fn test_silence() {
        assert_eq!(response_for("   "), "Fine. Be that way!");
    }

    #[test]
    fn test_normal() {
        assert_eq!(response_for("Hi"), "Whatever.");
    }

    #[test]
    fn test_v2_matches() {
        for s in &["WATCH OUT!", "Does this work?", "WHAT IS THIS?", "   ", "Hi"] {
            assert_eq!(response_for(s), response_for_v2(s));
        }
    }
}
(* Bob — String Pattern Matching *)

let is_question s = String.length (String.trim s) > 0 &&
  String.get (String.trim s) (String.length (String.trim s) - 1) = '?'

let is_yelling s =
  let has_letter = String.to_seq s |> Seq.exists (fun c ->
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) in
  has_letter && String.uppercase_ascii s = s

let is_silence s = String.trim s = ""

let response_for s =
  match is_silence s, is_yelling s, is_question s with
  | true, _, _ -> "Fine. Be that way!"
  | _, true, true -> "Calm down, I know what I'm doing!"
  | _, true, false -> "Whoa, chill out!"
  | _, false, true -> "Sure."
  | _ -> "Whatever."

let () =
  assert (response_for "WATCH OUT!" = "Whoa, chill out!");
  assert (response_for "Does this work?" = "Sure.");
  assert (response_for "   " = "Fine. Be that way!")

📊 Detailed Comparison

Bob — Comparison

Core Insight

Bob demonstrates tuple pattern matching on computed boolean conditions. The approach is identical in both languages — compute predicates, match on the tuple. String operations differ slightly in ergonomics.

OCaml Approach

  • `String.trim`, `String.uppercase_ascii` for string ops
  • `String.to_seq |> Seq.exists` for character testing
  • Manual last-character check: `String.get s (String.length s - 1)`
  • Tuple match: `match a, b, c with | true, _, _ -> ...`

Rust Approach

  • `.trim()`, `.to_uppercase()` — method syntax
  • `.chars().any(|c| c.is_alphabetic())` for character testing
  • `.ends_with('?')` — dedicated method
  • Tuple match: `match (a, b, c) { (true, _, _) => ... }`

Comparison Table

AspectOCamlRust
Trim`String.trim``.trim()`
Uppercase`String.uppercase_ascii``.to_uppercase()`
Ends withManual char check`.ends_with()`
Has letter`Seq.exists``.chars().any()`
Tuple match`match a, b, c with``match (a, b, c)`
Result type`string``&'static str`

Learner Notes

  • Rust's `.ends_with()` is more ergonomic than OCaml's manual index check
  • `&'static str` for constant strings avoids allocation
  • Both languages handle tuple matching the same way — very elegant pattern
  • Rust's `.is_alphabetic()` handles Unicode; OCaml's manual check doesn't