๐Ÿฆ€ Functional Rust

007: Flatten Nested List

Difficulty: 2 Level: Beginner Flatten an arbitrarily nested list structure into a flat list using a recursive enum.

The Problem This Solves

Real data is rarely flat. A file system has folders inside folders. A JSON document has arrays inside arrays. When you need to process every leaf value, you want a flat list โ€” not a tree you have to recurse through manually every time. Without a proper recursive type and a `flatten` function, you end up writing the same recursive traversal everywhere, scattered across your codebase. The traversal logic leaks into business logic. Defining a `Node<T>` enum that can hold either a single value (`One`) or a list of further nodes (`Many`) lets you model the nesting once, and `flatten` handles the traversal cleanly.

The Intuition

Think of a Russian nesting doll. Each doll either contains a solid core (a `One`) or more dolls (a `Many`). Flattening means opening every doll until you've collected all the cores. In plain English: walk the list. When you see a `One(x)`, yield `x`. When you see a `Many(xs)`, recurse into `xs` and yield everything inside. Collect all yields into a flat `Vec`. This is a classic structural recursion โ€” the function's shape mirrors the data's shape.

How It Works in Rust

#[derive(Debug, PartialEq, Clone)]
enum Node<T> {
 One(T),            // leaf: a single value
 Many(Vec<Node<T>>) // branch: more nodes inside
}

fn flatten<T: Clone>(list: &[Node<T>]) -> Vec<T> {
 list.iter()
     .flat_map(|node| match node {
         Node::One(x)   => vec![x.clone()],  // base case: wrap value
         Node::Many(xs) => flatten(xs),       // recursive case: descend
     })
     .collect()
}
`flat_map` does two things at once: transform each element AND flatten one level. The `T: Clone` bound lets us extract owned values from borrowed nodes without consuming the input.

What This Unlocks

Key Differences

ConceptOCamlRust
Recursive type`type 'a node = One of 'a \Many of 'a node list``enum Node<T> { One(T), Many(Vec<Node<T>>) }`
Heap allocationImplicit (GC)Explicit (`Vec` allocates on heap)
Value extractionFree under GCRequires `T: Clone` or ownership transfer
Tail-call safetyTCO guaranteedNo TCO โ€” deep nesting can stack overflow
Traversal styleTail-recursive helper with accumulator`flat_map` + recursive call
// Flatten Nested List
// Rust translation from OCaml 99 Problems #7

// Define nested list type
#[derive(Debug, PartialEq, Clone)]
enum Node<T> {
    One(T),
    Many(Vec<Node<T>>),
}

// Flatten recursively
fn flatten<T: Clone>(list: &[Node<T>]) -> Vec<T> {
    list.iter()
        .flat_map(|node| match node {
            Node::One(x) => vec![x.clone()],
            Node::Many(xs) => flatten(xs),
        })
        .collect()
}

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

    #[test]
    fn test_flatten() {
        use Node::*;
        assert_eq!(
            flatten(&[One(1), Many(vec![One(2), Many(vec![One(3), One(4)])]), One(5)]),
            vec![1, 2, 3, 4, 5]
        );
        assert_eq!(flatten::<i32>(&[]), Vec::<i32>::new());
        assert_eq!(flatten(&[One(1)]), vec![1]);
    }
}

fn main() {
    use Node::*;
    let nested = vec![One(1), Many(vec![One(2), One(3)])];
    println!("flatten(nested) = {:?}", flatten(&nested));
    println!("โœ“ Rust tests passed");
}
(* Flatten Nested List *)
(* OCaml 99 Problems #7 *)

(* Define nested list type *)
type 'a node =
  | One of 'a
  | Many of 'a node list

(* Flatten recursively *)
let flatten lst =
  let rec aux acc = function
    | [] -> acc
    | One x :: t -> aux (x :: acc) t
    | Many xs :: t -> aux (aux acc xs) t
  in
  List.rev (aux [] lst)

(* Tests *)
let () =
  assert (flatten [One 1; Many [One 2; Many [One 3; One 4]]; One 5] = [1; 2; 3; 4; 5]);
  assert (flatten [] = []);
  assert (flatten [One 1] = [1]);
  print_endline "โœ“ OCaml tests passed"

๐Ÿ“Š Detailed Comparison

Comparison: Flatten Nested List

OCaml โ€” Recursive with accumulator

๐Ÿช Show OCaml equivalent
type 'a node = One of 'a | Many of 'a node list

let flatten lst =
let rec aux acc = function
 | [] -> acc
 | One x :: t -> aux (x :: acc) t
 | Many xs :: t -> aux (aux acc xs) t
in
List.rev (aux [] lst)

Rust โ€” Idiomatic (flat_map)

pub fn flatten<T: Clone>(list: &[Node<T>]) -> Vec<T> {
 list.iter()
     .flat_map(|node| match node {
         Node::One(x) => vec![x.clone()],
         Node::Many(xs) => flatten(xs),
     })
     .collect()
}

Rust โ€” Stack-based (no recursion)

pub fn flatten_stack<T: Clone>(list: &[Node<T>]) -> Vec<T> {
 let mut result = Vec::new();
 let mut stack: Vec<&Node<T>> = list.iter().rev().collect();
 while let Some(node) = stack.pop() {
     match node {
         Node::One(x) => result.push(x.clone()),
         Node::Many(xs) => {
             for child in xs.iter().rev() {
                 stack.push(child);
             }
         }
     }
 }
 result
}

Comparison Table

AspectOCamlRust
Type definition`type 'a node = One of 'a \Many of 'a node list``enum Node<T> { One(T), Many(Vec<Node<T>>) }`
RecursionTail-recursive with accumulatorStack-based iteration (safer)
MemoryGC-managed cons cellsOwned `Vec` on heap
CopyingImplicit (GC)Explicit `Clone` trait bound
OwnershipShared by defaultMust choose: borrow (`&`) or consume

Takeaways

1. Rust enums are algebraic data types โ€” same expressive power as OCaml variants 2. Without guaranteed TCO, prefer explicit stacks for potentially deep recursion in Rust 3. The ownership system forces you to decide: clone data or consume it โ€” OCaml hides this choice 4. `flat_map` gives the most readable recursive solution but allocates intermediate vectors 5. The consuming version (`flatten_owned`) is uniquely Rust โ€” it's impossible in GC languages where data is always shared