ExamplesBy LevelBy TopicLearning Paths
044 Intermediate

044 — Option Filter

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "044 — Option Filter" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. `Option::filter` applies a predicate to the value inside a `Some`, turning it into `None` if the predicate fails. Key difference from OCaml: 1. **Predicate argument**: Rust's filter closure receives `&T` (reference to the inner value). OCaml's filter function receives `T` directly. Be careful with the reference in Rust: `filter(|&x| x > 0)` pattern

Tutorial

The Problem

Option::filter applies a predicate to the value inside a Some, turning it into None if the predicate fails. It is the conditional guard operation for Option: "keep this value only if it satisfies this condition". Combined with map and and_then, filter completes the basic Option toolkit.

This pattern appears in validation pipelines: parse_int(s).filter(|&x| x >= 0).filter(|&x| x < 100) — parse a string, then validate the range, producing None at any failure point. It is a declarative alternative to nested if statements on unwrapped values.

🎯 Learning Outcomes

  • • Use opt.filter(|x| predicate(x)) to discard values not meeting a condition
  • • Chain filter with map and and_then for validation pipelines
  • • Understand that filter never introduces Some — it can only remove it
  • • Use filter as a guard in option chains to enforce preconditions
  • • Recognize filter as the "zero of the monad" — it can collapse a chain to None
  • • Use Option::filter(pred) to return None when the predicate fails on a Some(x) value
  • • Compose .filter().map().and_then() for declarative option processing without explicit match
  • Code Example

    #![allow(clippy::all)]
    // Placeholder — pending conversion

    Key Differences

  • Predicate argument: Rust's filter closure receives &T (reference to the inner value). OCaml's filter function receives T directly. Be careful with the reference in Rust: filter(|&x| x > 0) pattern-matches the reference.
  • **filter as bind**: opt.filter(pred) is equivalent to opt.and_then(|x| if pred(&x) { Some(x) } else { None }). Understanding this connection shows filter is not a primitive — it is derivable.
  • **Option.filter availability**: OCaml 4.08 added Option.filter. Earlier versions need match or the bind derivation. Rust has always had Option::filter.
  • **retain analogy**: Option::filter is analogous to Vec::retain — both remove elements that fail a predicate. retain is in-place; filter produces a new Option.
  • **Option::filter semantics:** opt.filter(pred) returns None if opt is None or if the predicate returns false. It is equivalent to opt.and_then(|x| if pred(&x) { Some(x) } else { None }).
  • OCaml equivalent: OCaml provides Option.filter pred opt (OCaml 4.08+) with identical semantics.
  • Composing filter + map: opt.filter(pred).map(transform) reads as "keep the value if it passes the predicate, then transform it." This is a common pattern for conditional transformation.
  • Use case: option_filter is particularly useful for validating optional inputs — keep the value only if it's in a valid range or satisfies a constraint.
  • OCaml Approach

    OCaml's Option.filter f opt: let filter f = function None -> None | Some x -> if f x then Some x else None. Chaining with pipe: opt |> Option.filter (fun x -> x > 0) |> Option.map (fun x -> x * 2). OCaml 4.08+ provides Option.filter. Without it: opt |> Option.bind (fun x -> if pred x then Some x else None) — filter is a special case of bind.

    Full Source

    #![allow(clippy::all)]
    // Placeholder — pending conversion

    Deep Comparison

    OCaml vs Rust: Option Filter

    Overview

    See the example.rs and example.ml files for detailed implementations.

    Key Differences

    AspectOCamlRust
    Type systemHindley-MilnerOwnership + traits
    MemoryGCZero-cost abstractions
    MutabilityExplicit refmut keyword
    Error handlingOption/ResultResult<T, E>

    See README.md for detailed comparison.

    Exercises

  • Age validation: Write validate_age(age: Option<i32>) -> Option<i32> that filters to [0, 150] using two chained filter calls.
  • Non-empty string: Write non_empty(s: Option<String>) -> Option<String> that returns None for Some("") and passes through non-empty strings. Use filter(|s| !s.is_empty()).
  • Conditional transform: Write square_if_positive(opt: Option<i32>) -> Option<i32> that squares the value only if it is positive, returning None otherwise. Combine filter and map.
  • Filter chain: Write filter_positive_even(opt: Option<i32>) -> Option<i32> using two chained .filter() calls — one for positive, one for even.
  • Filter with transform: Write filter_map_option<T, U>(opt: Option<T>, pred: impl Fn(&T) -> bool, transform: impl Fn(T) -> U) -> Option<U> combining filter and map in one step.
  • Open Source Repos