ExamplesBy LevelBy TopicLearning Paths
022 Intermediate

022 — Create a List Containing All Integers in a Range

Functional Programming

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

  • • Use Rust's built-in range syntax (a..=b) for lazy range generation
  • • Collect a range to Vec<i32> with .collect()
  • • Implement an explicit recursive range generator to understand the structure
  • • Handle the case a > b (empty range or descending range)
  • • Understand lazy (iterator) vs eager (Vec) range representations
  • • Use (a..=b).collect::<Vec<i32>>() to eagerly materialize a lazy range
  • • Understand that (a..=b) is a zero-allocation lazy iterator — use it directly in map, filter, sum without collecting
  • Code Example

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

    Key Differences

  • Built-in range: Rust's (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.
  • Lazy vs eager: Rust's range is lazy — (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.
  • Descending: Rust has no lazy descending range; use (a..=b).rev(). OCaml's recursive version naturally builds descending if you count up.
  • Step: Rust's (0..20).step_by(3) produces 0, 3, 6, ... OCaml requires manual arithmetic: List.init n (fun i -> a + i * step).
  • Lazy vs eager: Rust's (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.
  • Built-in syntax: Rust's 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 conversion

    Deep Comparison

    OCaml vs Rust: Range

    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

  • Floating-point range: Write 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.
  • Sparse range: Write range_except(a: i32, b: i32, exclude: &[i32]) -> Vec<i32> that generates [a..=b] minus the excluded values. Use filter.
  • Infinite counter: Implement counter(start: i32, step: i32) -> impl Iterator<Item=i32> as an infinite iterator that can be take(n) limited. Compare with std::iter::successors.
  • Descending range: Implement range_desc(a: i32, b: i32) -> Vec<i32> that generates [b, b-1, ..., a]. Use .rev() on a range, or std::iter::successors.
  • Step range: Implement 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.
  • Open Source Repos