๐Ÿฆ€ Functional Rust
๐ŸŽฌ Error Handling in Rust Option, Result, the ? operator, and combinators.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Option represents a value that may or may not exist โ€” Some(value) or None

โ€ข Result represents success (Ok) or failure (Err) โ€” no exceptions needed

โ€ข The ? operator propagates errors up the call stack concisely

โ€ข Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations

โ€ข The compiler forces you to handle every error case โ€” no silent failures

Example 015: Replicate Elements N Times

Difficulty: โญ Category: Lists, Iteration Concept: Generalization of duplicate: replicate each element n times. Shows the power of `repeat`/`take` combinators and how a simple recursive helper in OCaml becomes an iterator chain in Rust. OCaml โ†’ Rust key insight: OCaml's `List.init n (fun _ -> x)` becomes Rust's `std::iter::repeat(x).take(n)` โ€” both create n copies of a value lazily.
// Replicate Elements N Times โ€” 99 Problems #15
// replicate [a, b, c] 3 โ†’ [a, a, a, b, b, b, c, c, c]

fn replicate<T: Clone>(lst: &[T], n: usize) -> Vec<T> {
    lst.iter().flat_map(|x| vec![x.clone(); n]).collect()
}

/// Tail-recursive style.
fn replicate_tr<T: Clone>(lst: &[T], n: usize) -> Vec<T> {
    let mut acc = Vec::with_capacity(lst.len() * n);
    for x in lst {
        for _ in 0..n {
            acc.push(x.clone());
        }
    }
    acc
}

/// Using concat_map + repeat โ€” explicit iterator approach.
fn replicate_map<T: Clone>(lst: &[T], n: usize) -> Vec<T> {
    lst.iter()
        .flat_map(|x| std::iter::repeat(x.clone()).take(n))
        .collect()
}

fn main() {
    let input = vec!['a', 'b', 'c'];
    println!("Input:      {:?}", input);
    println!("x3:         {:?}", replicate(&input, 3));
    println!("x1:         {:?}", replicate(&input, 1));
    println!("x0:         {:?}", replicate(&input, 0));
    println!("TR x2:      {:?}", replicate_tr(&[1, 2], 2));
    println!("Map x3:     {:?}", replicate_map(&input, 3));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_replicate_zero() {
        assert_eq!(replicate(&[1, 2, 3], 0), vec![]);
    }

    #[test]
    fn test_replicate_one() {
        assert_eq!(replicate(&[1, 2, 3], 1), vec![1, 2, 3]);
    }

    #[test]
    fn test_replicate_three() {
        assert_eq!(
            replicate(&['a', 'b', 'c'], 3),
            vec!['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c']
        );
    }

    #[test]
    fn test_all_versions_match() {
        let input = vec![1, 2];
        assert_eq!(replicate(&input, 3), replicate_tr(&input, 3));
        assert_eq!(replicate(&input, 3), replicate_map(&input, 3));
    }
}
(* Replicate Elements N Times โ€” 99 Problems #15 *)
(* replicate [a;b;c] 3 โ†’ [a;a;a;b;b;b;c;c;c] *)

(* โ”€โ”€ Recursive with helper โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ *)

let replicate lst n =
  let rec repeat x = function
    | 0 -> []
    | k -> x :: repeat x (k - 1)
  in
  let rec aux = function
    | [] -> []
    | h :: t -> repeat h n @ aux t
  in aux lst

(* โ”€โ”€ Tail-recursive โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ *)

let replicate_tr lst n =
  let rec aux acc = function
    | [] -> List.rev acc
    | h :: t ->
      let rec add_n acc = function
        | 0 -> aux acc t
        | k -> add_n (h :: acc) (k - 1)
      in add_n acc n
  in aux [] lst

(* โ”€โ”€ Using concat_map + List.init โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ *)

let replicate_map lst n =
  List.concat_map (fun x -> List.init n (fun _ -> x)) lst

(* โ”€โ”€ Tests โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ *)
let () =
  assert (replicate [] 5 = []);
  assert (replicate [1;2;3] 0 = []);
  assert (replicate [1;2;3] 1 = [1;2;3]);
  assert (replicate ['a';'b';'c'] 3 = ['a';'a';'a';'b';'b';'b';'c';'c';'c']);
  assert (replicate_tr [1;2] 3 = [1;1;1;2;2;2]);
  assert (replicate_map [1;2] 2 = [1;1;2;2]);
  print_endline "โœ“ Replicate tests passed"

๐Ÿ“Š Detailed Comparison

Replicate Elements N Times: OCaml vs Rust

The Core Insight

Replication is the general case of duplication โ€” produce n copies of each element. It's a clean demonstration of repetition combinators: OCaml's `List.init` and Rust's `std::iter::repeat().take()`. The problem also highlights pre-allocation in Rust: since you know the output size exactly (`len * n`), you can avoid all reallocation.

OCaml Approach

A recursive helper generates n copies, then the main function maps over the list:
๐Ÿช Show OCaml equivalent
let replicate lst n =
let rec repeat x = function
 | 0 -> []
 | k -> x :: repeat x (k - 1)
in List.concat_map (fun x -> repeat x n) lst
Or more concisely with `List.init`:
๐Ÿช Show OCaml equivalent
let replicate lst n = List.concat_map (fun x -> List.init n (fun _ -> x)) lst

Rust Approach

`std::iter::repeat` combined with `take` and `flat_map` is elegant:
pub fn replicate<T: Clone>(list: &[T], n: usize) -> Vec<T> {
 list.iter()
     .flat_map(|x| std::iter::repeat(x.clone()).take(n))
     .collect()
}
The pre-allocated imperative version is optimal for performance:
let mut result = Vec::with_capacity(list.len() * n);
for item in list { for _ in 0..n { result.push(item.clone()); } }

Key Differences

AspectOCamlRust
Repeat combinator`List.init n (fun _ -> x)``repeat(x).take(n)`
Expansion`concat_map``flat_map`
MemoryIntermediate lists (GC)Lazy iterator (zero-cost)
Pre-allocationNot possible (linked list)`Vec::with_capacity(len * n)`
n = 0Returns `[]` naturallyReturns empty Vec

What Rust Learners Should Notice

  • `std::iter::repeat` is lazy: Unlike `vec![x; n]` which allocates immediately, `repeat(x).take(n)` yields elements one at a time. Inside `flat_map`, this means no intermediate allocation.
  • Pre-allocation matters: `Vec::with_capacity(list.len() * n)` allocates once. Without it, the Vec may reallocate multiple times as it grows. OCaml lists can't pre-allocate.
  • Clone cost scales with n: Each element is cloned n times. For expensive-to-clone types, consider `Rc<T>` to share instead of copy.
  • Edge case: n = 0: All implementations naturally return empty for n=0 โ€” `take(0)` yields nothing, and the recursive base case `0 -> []` handles it.

Further Reading

  • [Rust โ€” std::iter::repeat](https://doc.rust-lang.org/std/iter/fn.repeat.html)
  • [99 OCaml Problems #15](https://ocaml.org/problems)
  • [Rust โ€” Vec::with_capacity](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.with_capacity)