022 — Create a List Containing All Integers in a Range
Tutorial Video
Text description (accessibility)
This video demonstrates the "022 — Create a List Containing All Integers in a Range" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. Generating a range of integers (OCaml 99 Problems #22) — `range(4, 9)` → `[4, 5, 6, 7, 8, 9]` — is one of the simplest list-generating operations and a gateway to understanding lazy vs eager sequence generation. Key difference from OCaml: 1. **Built
Tutorial
The Problem
Generating a range of integers (OCaml 99 Problems #22) — range(4, 9) → [4, 5, 6, 7, 8, 9] — is one of the simplest list-generating operations and a gateway to understanding lazy vs eager sequence generation. In Python it is range(4, 9), in Haskell [4..9], in OCaml (with batteries) 4 -- 9. The key insight is that a range can be represented lazily (an iterator) or eagerly (a list/vector).
This distinction matters enormously in practice: Rust's (4..=9) is a lazy Range<i32> that allocates nothing until consumed. Generating (0..1_000_000) as a range object is O(1); collecting it into a Vec is O(n). Understanding this is essential for writing efficient pipelines.
🎯 Learning Outcomes
(a..=b) for lazy range generationVec<i32> with .collect()a > b (empty range or descending range)(a..=b).collect::<Vec<i32>>() to eagerly materialize a lazy range(a..=b) is a zero-allocation lazy iterator — use it directly in map, filter, sum without collectingCode Example
#![allow(clippy::all)]
// Placeholder — pending conversionKey Differences
(a..=b) syntax generates a lazy RangeInclusive<i32> at zero cost. OCaml has no built-in range syntax; you construct it manually or use Batteries/Core.(0..1_000_000).take(5) never computes element 5. OCaml's range a b from the 99 Problems is eager — it builds the full list.(a..=b).rev(). OCaml's recursive version naturally builds descending if you count up.(0..20).step_by(3) produces 0, 3, 6, ... OCaml requires manual arithmetic: List.init n (fun i -> a + i * step).(a..=b) is a lazy RangeInclusive<i32> — O(1) to create, O(n) to consume. OCaml's recursive range eagerly allocates the full list — O(n) immediately.collect() drives evaluation:** In Rust, (4..=9).collect::<Vec<i32>>() is explicit about when evaluation happens. In OCaml, the list is always eagerly built.a..b and a..=b are built-in range literals. Haskell has [a..b]. OCaml has no built-in range syntax in the core language.OCaml Approach
OCaml's version: let range a b = let rec aux acc n = if n < a then acc else aux (n :: acc) (n - 1) in aux [] b. The recursion counts down from b to a, building the result in forward order by prepending. List.init (b - a + 1) (fun i -> a + i) is a more concise version. With Seq: Seq.unfold (fun i -> if i > b then None else Some (i, i + 1)) a |> List.of_seq.
OCaml's range using recursion: let rec range a b = if a > b then [] else a :: range (a+1) b. Simple and idiomatic. For large ranges, use the tail-recursive version: let range a b = let rec aux acc b = if b < a then acc else aux (b :: acc) (b-1) in aux [] b. OCaml's Batteries library provides a -- b notation for ranges.
Full Source
#![allow(clippy::all)]
// Placeholder — pending conversionDeep Comparison
OCaml vs Rust: Range
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
frange(a: f64, b: f64, step: f64) -> Vec<f64> that generates a floating-point range. Handle floating-point accumulation errors by computing a + i * step rather than adding step repeatedly.range_except(a: i32, b: i32, exclude: &[i32]) -> Vec<i32> that generates [a..=b] minus the excluded values. Use filter.counter(start: i32, step: i32) -> impl Iterator<Item=i32> as an infinite iterator that can be take(n) limited. Compare with std::iter::successors.range_desc(a: i32, b: i32) -> Vec<i32> that generates [b, b-1, ..., a]. Use .rev() on a range, or std::iter::successors.range_step(start: i32, end: i32, step: i32) -> Vec<i32> generating [start, start+step, start+2*step, ...] up to but not exceeding end. Handle negative steps for descending ranges.