๐Ÿฆ€ Functional Rust

021: Insert At

Difficulty: 1 Level: Foundations Insert an element at a specific position (1-based) in a list, shifting everything else right.

The Problem This Solves

You're maintaining an ordered list โ€” a playlist, a ranked leaderboard, a priority queue โ€” and need to insert a new item at a specific position without replacing anything. The task: given `['a','b','c','d']`, element `'X'`, and position `2`, produce `['a','X','b','c','d']`. In Python: `lst.insert(k-1, elem)` โ€” straightforward, but mutates the original list. If the caller still holds a reference to the original, they now see the modified list. Python doesn't warn you. Rust's version takes a slice (a reference), clones it into a new Vec, and returns the result โ€” the original is untouched. The function signature itself communicates "I won't modify your data." And if `k` is beyond the list end, inserting at the tail is the natural fallback โ€” no panic, no error, just sensible behavior.

The Intuition

In Python: `new_list = lst[:k-1] + [elem] + lst[k-1:]` (non-mutating version) In JavaScript: `[...lst.slice(0, k-1), elem, ...lst.slice(k-1)]` Inserting at position `k` (1-based) means: "everything before position k stays, then the new element, then everything from position k onward." In 0-based terms, the new element goes at index `k-1`. Rust's `Vec::insert(pos, elem)` does this in one call. The tricky edge cases โ€” inserting at position 0, inserting past the end โ€” are handled by `.saturating_sub(1).min(lst.len())`: `saturating_sub` prevents underflow on 0, and `.min(lst.len())` clamps beyond-end insertions to append.

How It Works in Rust

fn insert_at<T: Clone>(elem: T, lst: &[T], k: usize) -> Vec<T> {
 // k=1 โ†’ index 0; k=99 on a 4-element list โ†’ index 4 (append)
 let pos = k.saturating_sub(1).min(lst.len());
 let mut result = lst.to_vec();  // clone: don't mutate the original
 result.insert(pos, elem);       // Vec::insert shifts everything right
 result
}

What This Unlocks

Key Differences

ConceptOCamlRust
Insert operationRecursive: rebuild list up to k, prepend elem`Vec::insert(pos, elem)` โ€” stdlib method
MutationOCaml lists immutable; new list returnedClone first (`to_vec()`), then mutate clone
1-based indexDecremented in recursive countdown`saturating_sub(1)` converts at entry
Out-of-bounds kInserts at end (recursive base case)`.min(lst.len())` clamps to append
Ownership`'a list` โ€” GC managed`Vec<T>` โ€” owned, heap-allocated
// Insert At โ€” 99 Problems #21
// Insert an element at position k (1-based).
// insert_at 'alfa' ['a','b','c','d'] 2
//   โ†’ ['a','alfa','b','c','d']

fn insert_at<T: Clone>(elem: T, lst: &[T], k: usize) -> Vec<T> {
    let pos = k.saturating_sub(1).min(lst.len());
    let mut result = lst.to_vec();
    result.insert(pos, elem);
    result
}

/// Recursive version.
fn insert_at_rec<T: Clone>(elem: T, lst: &[T], k: usize) -> Vec<T> {
    fn aux<T: Clone>(elem: T, lst: &[T], k: usize, acc: Vec<T>) -> Vec<T> {
        if k <= 1 || lst.is_empty() {
            let mut result = acc;
            result.push(elem);
            result.extend_from_slice(lst);
            result
        } else {
            let mut new_acc = acc;
            new_acc.push(lst[0].clone());
            aux(elem, &lst[1..], k - 1, new_acc)
        }
    }
    aux(elem, lst, k, vec![])
}

fn main() {
    let input = vec!['a', 'b', 'c', 'd'];
    println!("Input:            {:?}", input);
    println!("Insert 'X' at 2:  {:?}", insert_at('X', &input, 2));
    println!("Insert 'X' at 1:  {:?}", insert_at('X', &input, 1));
    println!("Insert 'X' at 5:  {:?}", insert_at('X', &input, 5));
    println!("Insert 'X' at 99: {:?}", insert_at('X', &input, 99));
    println!("Rec insert at 2:  {:?}", insert_at_rec('X', &input, 2));
}

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

    #[test]
    fn test_insert_at_middle() {
        let input = vec!['a', 'b', 'c', 'd'];
        assert_eq!(
            insert_at('X', &input, 2),
            vec!['a', 'X', 'b', 'c', 'd']
        );
    }

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

    #[test]
    fn test_insert_at_beyond_end() {
        assert_eq!(insert_at(4, &[1, 2, 3], 99), vec![1, 2, 3, 4]);
    }

    #[test]
    fn test_recursive_matches() {
        let input = vec!['a', 'b', 'c', 'd'];
        assert_eq!(insert_at('X', &input, 2), insert_at_rec('X', &input, 2));
    }
}
(* Insert At *)
(* OCaml 99 Problems #21 *)

(* Implementation for example 21 *)

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