๐Ÿฆ€ 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

017: Split List

Difficulty: 1 Level: Foundations Split a list into two parts at a given index โ€” like Python's `lst[:n]` and `lst[n:]`.

The Problem This Solves

You receive a stream of records and need to separate the first N items from the rest โ€” maybe the first page of results vs. the overflow, or the training set vs. the test set. The task: given `['a','b','c','d','e','f','g','h','i','k']` and `n=3`, produce `(['a','b','c'], ['d','e','f','g','h','i','k'])`. Python makes this trivial with slice syntax: `lst[:n], lst[n:]`. But what if `n` is larger than the list? Python silently returns an empty second part โ€” fine if you know it, a silent bug if you don't. Rust's slice indexing does the same thing, and the bounds check is built in. You can't accidentally go out of bounds โ€” the `.min(lst.len())` guard ensures `n` never exceeds what's available, and the compiler enforces that you handle both parts.

The Intuition

In Python: `left, right = lst[:n], lst[n:]` In JavaScript: `[lst.slice(0, n), lst.slice(n)]` Rust slices work identically. `lst[..n]` is everything before index `n`, and `lst[n..]` is everything from `n` onward. The `.to_vec()` call creates an owned copy โ€” necessary here because the function returns new `Vec`s, not references into the original. The key safety detail: `n.min(lst.len())` clamps `n` so we never try to index past the end. In Python that's implicit; in Rust you make it explicit โ€” and that explicitness is a feature, not a burden.

How It Works in Rust

fn split<T: Clone>(lst: &[T], n: usize) -> (Vec<T>, Vec<T>) {
 let n = n.min(lst.len());   // clamp: never go past end
 (lst[..n].to_vec(), lst[n..].to_vec())
}
The recursive version (`split_rec`) builds the left side element by element using an accumulator and returns the right side when the counter hits zero. Same result, more steps โ€” useful for understanding how iteration maps to recursion.

What This Unlocks

Key Differences

ConceptOCamlRust
Slice syntax`List.filteri` / recursive take+drop`lst[..n]` and `lst[n..]`
Bounds safetyRuntime exception on bad index`.min(lst.len())` clamp, then safe slice
Return type`'a list * 'a list` (tuple)`(Vec<T>, Vec<T>)` (tuple)
Copying elements`List.map` with identity`.to_vec()` โ€” clones the slice
Destructuring`let (l, r) = split ...``let (left, right) = split(...)`
// Split List โ€” 99 Problems #17
// Split a list into two parts at position n.
// split ['a','b','c','d','e','f','g','h','i','k'] 3
//   โ†’ (['a','b','c'], ['d','e','f','g','h','i','k'])

fn split<T: Clone>(lst: &[T], n: usize) -> (Vec<T>, Vec<T>) {
    let n = n.min(lst.len());
    (lst[..n].to_vec(), lst[n..].to_vec())
}

/// Recursive version.
fn split_rec<T: Clone>(lst: &[T], n: usize) -> (Vec<T>, Vec<T>) {
    fn aux<T: Clone>(lst: &[T], n: usize, acc: Vec<T>) -> (Vec<T>, Vec<T>) {
        match (lst, n) {
            ([], _) => (acc, vec![]),
            (rest, 0) => (acc, rest.to_vec()),
            ([head, tail @ ..], k) => {
                let mut new_acc = acc;
                new_acc.push(head.clone());
                aux(tail, k - 1, new_acc)
            }
        }
    }
    aux(lst, n, vec![])
}

fn main() {
    let input = vec!['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k'];
    let (left, right) = split(&input, 3);
    println!("Input:  {:?}", input);
    println!("Left:   {:?}", left);
    println!("Right:  {:?}", right);

    let (l2, r2) = split(&input, 0);
    println!("Split at 0: {:?}, {:?}", l2, r2);

    let (l3, r3) = split(&input, 100);
    println!("Split at 100 (> len): {:?}, {:?}", l3, r3);

    let (lr, rr) = split_rec(&input, 3);
    println!("Rec split: {:?}, {:?}", lr, rr);
}

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

    #[test]
    fn test_split_basic() {
        let input = vec!['a', 'b', 'c', 'd', 'e'];
        assert_eq!(
            split(&input, 3),
            (vec!['a', 'b', 'c'], vec!['d', 'e'])
        );
    }

    #[test]
    fn test_split_at_zero() {
        let input = vec![1, 2, 3];
        assert_eq!(split(&input, 0), (vec![], vec![1, 2, 3]));
    }

    #[test]
    fn test_split_beyond_length() {
        let input = vec![1, 2, 3];
        assert_eq!(split(&input, 10), (vec![1, 2, 3], vec![]));
    }

    #[test]
    fn test_split_rec_matches() {
        let input = vec!['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k'];
        assert_eq!(split(&input, 3), split_rec(&input, 3));
    }
}
(* Split List *)
(* OCaml 99 Problems #17 *)

(* Implementation for example 17 *)

(* Tests *)
let () =
  (* Add tests *)
  print_endline "โœ“ OCaml tests passed"