020 — Remove the Kth Element
Tutorial Video
Text description (accessibility)
This video demonstrates the "020 — Remove the Kth Element" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. Removing the element at position k from a list (OCaml 99 Problems #20) is a fundamental list surgery operation — the building block for deletion in arrays, linked lists, and sequences. Key difference from OCaml: 1. **`Vec::remove` vs list surgery**: Rust's `Vec::remove(i)` is O(n) due to shifting. OCaml's list removal reconstructs the prefix list, also O(n) but with different constant factors.
Tutorial
The Problem
Removing the element at position k from a list (OCaml 99 Problems #20) is a fundamental list surgery operation — the building block for deletion in arrays, linked lists, and sequences. It produces a new list with one element missing and returns both the removed element and the modified list.
This pattern appears everywhere: removing a player from a queue, deleting a row from a table, extracting the winner from a lottery draw (example 024), and implementing undo operations. The challenge is handling the removed element cleanly — returning it alongside the modified list as a tuple.
🎯 Learning Outcomes
(T, Vec<T>) tuple to simultaneously yield the removed element and the new listenumerate().filter() or explicit split for position-based removalOption<(T, Vec<T>)>Option<(T, Vec<T>)> for safe out-of-bounds handling rather than panickingVec::remove(k) (O(n) shift, in-place) from Vec::swap_remove(k) (O(1), changes order)Code Example
#![allow(clippy::all)]
// Placeholder — pending conversionKey Differences
Vec::remove vs list surgery**: Rust's Vec::remove(i) is O(n) due to shifting. OCaml's list removal reconstructs the prefix list, also O(n) but with different constant factors.Vec::remove panics on out-of-bounds. Return Option or check bounds first. OCaml uses failwith (raises an exception).rev_append**: OCaml's List.rev_append acc t is a key idiom — it combines reversing the accumulator with appending the tail in one pass. Rust achieves the same with [&v[..k], &v[k+1..]].concat().Vec::remove in-place:** Rust's v.remove(k) removes and returns the element at index k, shifting elements left — O(n). Returns the removed value directly (panics on out-of-bounds).Option for safety:** The safe version returns Option<(T, Vec<T>)> to handle out-of-bounds. OCaml's version typically raises an exception on invalid index.[&v[..k], &v[k+1..]].concat() creates a new Vec skipping index k — O(n) with explicit O(n) allocation. Both forms are equivalent.OCaml Approach
OCaml's version: let remove_at k lst = let rec aux acc n = function | [] -> failwith "index out of bounds" | x :: t -> if n = k then (x, List.rev_append acc t) else aux (x :: acc) (n + 1) t in aux [] 1 lst. List.rev_append acc t concatenates the reversed accumulator with the tail — this is O(n) and avoids a separate reverse call.
OCaml's version returns a tuple: let rec remove_at k = function | [] -> (None, []) | h :: t -> if k = 1 then (Some h, t) else let (removed, rest) = remove_at (k-1) t in (removed, h :: rest). The tail-recursive version uses an accumulator for the prefix, reversing at the end. OCaml's 99 Problems uses 1-based indexing.
Full Source
#![allow(clippy::all)]
// Placeholder — pending conversionDeep Comparison
OCaml vs Rust: Remove Kth
Overview
See the example.rs and example.ml files for detailed implementations.
Key Differences
| Aspect | OCaml | Rust |
|---|---|---|
| Type system | Hindley-Milner | Ownership + traits |
| Memory | GC | Zero-cost abstractions |
| Mutability | Explicit ref | mut keyword |
| Error handling | Option/Result | Result<T, E> |
See README.md for detailed comparison.
Exercises
remove_all(v: &[i32], x: i32) -> Vec<i32> that removes all occurrences of x using .filter(). Compare with retain() (in-place filter).remove_first(v: &[i32], x: i32) -> Option<Vec<i32>> that removes only the first occurrence. Use .iter().position() to find the index.move_to_end(v: &mut Vec<i32>, k: usize) that removes the element at position k and appends it to the end, using Vec::remove and Vec::push.remove_all(v: &[T], indices: &[usize]) -> Vec<T> that removes elements at multiple indices in one pass. Sort indices first to process them efficiently.swap_remove_safe(v: &mut Vec<T>, k: usize) -> Option<T> — O(1) removal by swapping the element with the last one. Useful when order doesn't matter.