🦀 Functional Rust
🎬 Fearless Concurrency Threads, Arc>, channels — safe parallelism enforced by the compiler.
📝 Text version (for readers / accessibility)

• std::thread::spawn creates OS threads — closures must be Send + 'static

• Arc> provides shared mutable state across threads safely

• Channels (mpsc) enable message passing — multiple producers, single consumer

• Send and Sync marker traits enforce thread safety at compile time

• Data races are impossible — the type system prevents them before your code runs

Example 279: Pascal's Triangle — Row Generation

Difficulty:Category: Math/Recursion OCaml Source: https://exercism.org/tracks/ocaml/exercises/pascals-triangle

Problem Statement

Generate the first N rows of Pascal's triangle, where each row is computed from the previous one using the "zip-with-add" trick: prepend and append 0 to the current row, then sum pairwise.

Learning Outcomes

OCaml Approach

OCaml uses `List.map2 (+)` to add two lists element-wise. The trick: `0 :: row` prepends a zero, `row @ [0]` appends a zero, making both lists the same length. `List.map2 (+)` then sums corresponding elements to produce the next row. Recursion accumulates rows.

Rust Approach

Rust uses `std::iter::once(&0).chain(row.iter()).zip(row.iter().chain(once(&0)))` — the same prepend/append-zero trick expressed with iterator adapters. `std::iter::successors` generates the infinite sequence of rows lazily, and `.take(n)` limits it.

Key Differences

1. Zip-with-add: OCaml's `List.map2 (+)` is a single call; Rust chains `once`, `chain`, `zip`, and `map` — more verbose but composable 2. Sequence generation: OCaml uses explicit recursion (`let rec go`); Rust's `successors` provides a declarative "generate from seed" pattern 3. Lazy vs eager: Rust's `successors` is lazy — rows are only computed when consumed; OCaml's recursion eagerly builds the full list 4. Numeric type: OCaml uses arbitrary-precision `int`; Rust uses `u64` which can overflow for large row numbers
/// Generate the next row of Pascal's triangle using zip-with-add.
fn next_row(row: &[u64]) -> Vec<u64> {
    std::iter::once(&0)
        .chain(row.iter())
        .zip(row.iter().chain(std::iter::once(&0)))
        .map(|(a, b)| a + b)
        .collect()
}

/// Generate n rows of Pascal's triangle.
fn pascal(n: usize) -> Vec<Vec<u64>> {
    std::iter::successors(Some(vec![1u64]), |prev| Some(next_row(prev)))
        .take(n)
        .collect()
}

fn main() {
    for row in pascal(8) {
        let s: Vec<String> = row.iter().map(|x| x.to_string()).collect();
        println!("{}", s.join(" "));
    }
}

/* Output:
   1
   1 1
   1 2 1
   1 3 3 1
   1 4 6 4 1
   1 5 10 10 5 1
   1 6 15 20 15 6 1
   1 7 21 35 35 21 7 1
*/
let next_row row =
  List.map2 (+) (0 :: row) (row @ [0])

let pascal n =
  let rec go row i =
    if i > n then []
    else row :: go (next_row row) (i + 1)
  in go [1] 1

let () =
  pascal 8 |> List.iter (fun row ->
    List.iter (Printf.printf "%d ") row;
    print_newline ()
  );
  (* Assertions *)
  assert (next_row [1] = [1; 1]);
  assert (next_row [1; 1] = [1; 2; 1]);
  assert (List.length (pascal 8) = 8);
  let row7 = List.nth (pascal 8) 7 in
  assert (row7 = [1; 7; 21; 35; 35; 21; 7; 1]);
  print_endline "ok"

📊 Detailed Comparison

OCaml vs Rust: Pascal's Triangle

Side-by-Side Code

OCaml

🐪 Show OCaml equivalent
let next_row row =
List.map2 (+) (0 :: row) (row @ [0])

let pascal n =
let rec go row i =
 if i > n then []
 else row :: go (next_row row) (i + 1)
in go [1] 1

Rust (idiomatic)

pub fn next_row(row: &[u64]) -> Vec<u64> {
 std::iter::once(&0)
     .chain(row.iter())
     .zip(row.iter().chain(std::iter::once(&0)))
     .map(|(a, b)| a + b)
     .collect()
}

pub fn pascal(n: usize) -> Vec<Vec<u64>> {
 std::iter::successors(Some(vec![1u64]), |prev| Some(next_row(prev)))
     .take(n)
     .collect()
}

Rust (functional/recursive)

pub fn pascal_recursive(n: usize) -> Vec<Vec<u64>> {
 fn go(row: Vec<u64>, i: usize, n: usize) -> Vec<Vec<u64>> {
     if i > n { return Vec::new(); }
     let next = next_row(&row);
     let mut result = vec![row];
     result.extend(go(next, i + 1, n));
     result
 }
 if n == 0 { return Vec::new(); }
 go(vec![1], 1, n)
}

Type Signatures

ConceptOCamlRust
Next row`val next_row : int list -> int list``fn next_row(row: &[u64]) -> Vec<u64>`
Triangle`val pascal : int -> int list list``fn pascal(n: usize) -> Vec<Vec<u64>>`
Zip-add`List.map2 (+) xs ys``xs.zip(ys).map(\(a, b)\a + b)`
Prepend zero`0 :: row``once(&0).chain(row.iter())`
Append zero`row @ [0]``row.iter().chain(once(&0))`

Key Insights

1. `List.map2` vs `zip`: OCaml's `List.map2 f xs ys` applies `f` pairwise in one call; Rust separates zipping from mapping — `.zip().map()` — which is more composable but requires two steps 2. Prepend/append symmetry: OCaml's `0 :: row` (O(1) prepend) and `row @ [0]` (O(n) append) have different costs; Rust's `once().chain()` is lazy in both directions — no allocation until `collect()` 3. `successors` as recursion replacement: Rust's `std::iter::successors(seed, f)` generates `[seed, f(seed), f(f(seed)), ...]` lazily — it's the iterator equivalent of OCaml's recursive `go` function, but doesn't consume stack frames 4. Borrowing in `next_row`: Takes `&[u64]` (borrows the slice) and returns an owned `Vec<u64>` — the caller keeps the previous row while the function builds the next one. In OCaml, GC handles this automatically 5. Overflow risk: OCaml's `int` won't overflow on 64-bit (it's 63-bit signed). Rust's `u64` will panic on overflow in debug mode — for very large triangles, consider `u128` or a bigint library

When to Use Each Style

Use idiomatic Rust when: You want clean, readable code — `successors` + `take` is the most Rust-native way to express "generate a sequence from a recurrence relation." It's lazy, composable, and stack-safe.

Use recursive Rust when: Teaching the algorithm structure — the recursive version maps 1:1 to the OCaml code and makes the "build row, recurse" pattern explicit. Good for understanding before refactoring to iterators.