044 — Option Filter
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
opt.filter(|x| predicate(x)) to discard values not meeting a conditionfilter with map and and_then for validation pipelinesfilter never introduces Some — it can only remove itfilter as a guard in option chains to enforce preconditionsOption::filter(pred) to return None when the predicate fails on a Some(x) value.filter().map().and_then() for declarative option processing without explicit matchCode Example
#![allow(clippy::all)]
// Placeholder — pending conversionKey Differences
&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 }).Option.filter pred opt (OCaml 4.08+) with identical semantics.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.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 conversionDeep Comparison
OCaml vs Rust: Option Filter
Overview
See the example.rs and example.ml files for detailed implementations.
Key Differences
| Aspect | OCaml | Rust |
|---|---|---|
| Type system | Hindley-Milner | Ownership + traits |
| Memory | GC | Zero-cost abstractions |
| Mutability | Explicit ref | mut keyword |
| Error handling | Option/Result | Result<T, E> |
See README.md for detailed comparison.
Exercises
validate_age(age: Option<i32>) -> Option<i32> that filters to [0, 150] using two chained filter calls.non_empty(s: Option<String>) -> Option<String> that returns None for Some("") and passes through non-empty strings. Use filter(|s| !s.is_empty()).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_positive_even(opt: Option<i32>) -> Option<i32> using two chained .filter() calls — one for positive, one for even.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.