๐Ÿฆ€ 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 014: Duplicate Elements

Difficulty: โญ Category: Lists, Higher-Order Functions Concept: Duplicate every element in a list. A simple but instructive problem showing one-to-many list transformations using flat_map/concat_map and basic recursion. OCaml โ†’ Rust key insight: OCaml's `h :: h :: duplicate t` becomes Rust's `flat_map(|x| vec![x.clone(), x.clone()])` โ€” the recursive cons vs iterator collect pattern.
// Duplicate Elements โ€” 99 Problems #14
// Duplicate every element: [a, b, c] โ†’ [a, a, b, b, c, c]

fn duplicate<T: Clone>(lst: &[T]) -> Vec<T> {
    lst.iter().flat_map(|x| [x.clone(), x.clone()]).collect()
}

/// Tail-recursive style with explicit accumulator.
fn duplicate_tr<T: Clone>(lst: &[T]) -> Vec<T> {
    let mut acc = Vec::with_capacity(lst.len() * 2);
    for x in lst {
        acc.push(x.clone());
        acc.push(x.clone());
    }
    acc
}

/// Using concat_map (flat_map) โ€” closest to OCaml style.
fn duplicate_map<T: Clone>(lst: &[T]) -> Vec<T> {
    lst.iter().flat_map(|x| vec![x.clone(), x.clone()]).collect()
}

fn main() {
    let nums = vec![1, 2, 3, 4];
    println!("Input:    {:?}", nums);
    println!("Doubled:  {:?}", duplicate(&nums));

    let chars = vec!['a', 'b', 'c'];
    println!("Chars:    {:?}", chars);
    println!("Doubled:  {:?}", duplicate(&chars));
    println!("TR:       {:?}", duplicate_tr(&chars));
    println!("Map:      {:?}", duplicate_map(&chars));

    let empty: Vec<i32> = vec![];
    println!("Empty:    {:?}", duplicate(&empty));
}

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

    #[test]
    fn test_duplicate_empty() {
        let empty: Vec<i32> = vec![];
        assert_eq!(duplicate(&empty), vec![]);
    }

    #[test]
    fn test_duplicate_single() {
        assert_eq!(duplicate(&[1]), vec![1, 1]);
    }

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

    #[test]
    fn test_all_versions_match() {
        let input = vec!['a', 'b', 'c'];
        let d = duplicate(&input);
        assert_eq!(d, duplicate_tr(&input));
        assert_eq!(d, duplicate_map(&input));
    }
}
(* Duplicate Elements โ€” 99 Problems #14 *)
(* Duplicate every element: [a;b;c] โ†’ [a;a;b;b;c;c] *)

(* โ”€โ”€ Recursive โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ *)

let rec duplicate = function
  | [] -> []
  | h :: t -> h :: h :: duplicate t

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

let duplicate_tr lst =
  let rec aux acc = function
    | [] -> List.rev acc
    | h :: t -> aux (h :: h :: acc) t
  in aux [] lst

(* โ”€โ”€ Using concat_map โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ *)

let duplicate_map lst =
  List.concat_map (fun x -> [x; x]) lst

(* โ”€โ”€ Tests โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ *)
let () =
  assert (duplicate [] = []);
  assert (duplicate [1] = [1; 1]);
  assert (duplicate [1; 2; 3] = [1; 1; 2; 2; 3; 3]);
  assert (duplicate_tr ['a'; 'b'; 'c'] = ['a'; 'a'; 'b'; 'b'; 'c'; 'c']);
  assert (duplicate_map [1; 2] = [1; 1; 2; 2]);
  print_endline "โœ“ Duplicate elements tests passed"

๐Ÿ“Š Detailed Comparison

Duplicate Elements: OCaml vs Rust

The Core Insight

Duplicating elements is a one-to-many transformation: each input produces exactly two outputs. It's a clean exercise in list construction โ€” OCaml uses cons (`::`) to prepend two copies, while Rust uses `flat_map` or push-based iteration. The simplicity makes ownership differences especially visible.

OCaml Approach

OCaml's recursive solution is elegantly minimal:
๐Ÿช Show OCaml equivalent
let rec duplicate = function
| [] -> []
| h :: t -> h :: h :: duplicate t
Two cons operations prepend two copies of the head. The tail-recursive version reverses at the end:
๐Ÿช Show OCaml equivalent
let duplicate_tr lst =
let rec aux acc = function
 | [] -> List.rev acc
 | h :: t -> aux (h :: h :: acc) t
in aux [] lst

Rust Approach

Rust's iterator approach uses `flat_map` for the one-to-many expansion:
pub fn duplicate<T: Clone>(list: &[T]) -> Vec<T> {
 list.iter().flat_map(|x| vec![x.clone(), x.clone()]).collect()
}
The imperative version with `push` is more efficient (avoids intermediate vec allocation):
for item in list {
 result.push(item.clone());
 result.push(item.clone());
}

Key Differences

AspectOCamlRust
Construction`h :: h :: tail` (O(1) prepend)`push` (O(1) amortized append)
CopyingAutomatic (values shared)Explicit `clone()`
DirectionBuilds front-to-back (cons)Builds back via push
MemoryNew list nodes (GC)Pre-allocatable Vec
One-to-many`concat_map (fun x -> [x;x])``flat_map(\x\vec![x,x])`

What Rust Learners Should Notice

  • `with_capacity` for known sizes: When you know the output will be exactly `2 * input.len()`, pre-allocate with `Vec::with_capacity`. OCaml can't do this with linked lists.
  • `flat_map` creates temporary Vecs: `flat_map(|x| vec![x, x])` allocates a small Vec per element. The `push`-based version avoids this overhead entirely.
  • Clone is the explicit cost: In OCaml, `h :: h :: t` shares the same value. In Rust, `Clone` creates independent copies. For `Copy` types (integers, etc.), this is zero-cost.
  • Simple problems reveal language philosophy: OCaml optimizes for expressiveness (one line!). Rust lets you choose between expressiveness (`flat_map`) and performance (`push` with pre-allocation).

Further Reading

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