โข Option
โข Result
โข 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
โข Option
โข Result
โข 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
fn drop_every<T: Clone>(lst: &[T], n: usize) -> Vec<T> {
if n == 0 {
return lst.to_vec(); // edge case: no-op
}
lst.iter()
.enumerate() // (0,'a'), (1,'b'), (2,'c'), ...
.filter(|(i, _)| (i + 1) % n != 0) // keep if NOT a multiple of n
.map(|(_, x)| x.clone()) // discard the index
.collect() // gather into Vec<T>
}
The `T: Clone` bound means "T must be copyable" โ needed because we're extracting values from a reference into a new owned `Vec`. For simple types like `char` or `i32`, this is trivially cheap.
There's also a recursive version in the code (`drop_every_rec`) that mirrors the OCaml style: pattern-match on the list, count down to n, drop that element, reset the counter. Both produce identical results โ the iterator version is more idiomatic Rust.
| Concept | OCaml | Rust |
|---|---|---|
| Enumerate | `List.mapi (fun i x -> (i, x))` | `.enumerate()` on any iterator |
| Filter by index | List comprehension / recursive | `.filter((i, _)` โ condition`)` |
| 1-based index | Explicit counter in recursion | `(i + 1)` offset in filter |
| Result type | `'a list` | `Vec<T>` (owned, heap-allocated) |
| Edge case (n=0) | Guard in pattern match | Early `return` before iterator chain |
// Drop Every Nth Element โ 99 Problems #16
// Drop every nth element from a list.
// drop_every ['a','b','c','d','e','f','g','h','i','k'] 3
// โ ['a','b','d','e','g','h','k']
fn drop_every<T: Clone>(lst: &[T], n: usize) -> Vec<T> {
if n == 0 {
return lst.to_vec();
}
lst.iter()
.enumerate()
.filter(|(i, _)| (i + 1) % n != 0)
.map(|(_, x)| x.clone())
.collect()
}
/// Recursive version matching OCaml style.
fn drop_every_rec<T: Clone>(lst: &[T], n: usize) -> Vec<T> {
fn aux<T: Clone>(lst: &[T], n: usize, count: usize) -> Vec<T> {
match lst {
[] => vec![],
[head, tail @ ..] => {
if count == n {
aux(tail, n, 1)
} else {
let mut result = vec![head.clone()];
result.extend(aux(tail, n, count + 1));
result
}
}
}
}
aux(lst, n, 1)
}
fn main() {
let input = vec!['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k'];
println!("Input: {:?}", input);
println!("Drop every 3rd: {:?}", drop_every(&input, 3));
println!("Drop every 2nd: {:?}", drop_every(&input, 2));
println!("Drop every 1st: {:?}", drop_every(&input, 1));
println!("Rec every 3rd: {:?}", drop_every_rec(&input, 3));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_drop_every_3rd() {
let input = vec!['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k'];
assert_eq!(
drop_every(&input, 3),
vec!['a', 'b', 'd', 'e', 'g', 'h', 'k']
);
}
#[test]
fn test_drop_every_1st() {
// Drop every 1st = drop all
assert_eq!(drop_every(&[1, 2, 3], 1), vec![]);
}
#[test]
fn test_drop_every_larger_than_list() {
// n > length: nothing dropped
assert_eq!(drop_every(&[1, 2, 3], 5), vec![1, 2, 3]);
}
#[test]
fn test_recursive_matches_iter() {
let input = vec!['a', 'b', 'c', 'd', 'e', 'f'];
assert_eq!(drop_every(&input, 3), drop_every_rec(&input, 3));
}
}
(* Drop Every Nth *)
(* OCaml 99 Problems #16 *)
(* Implementation for example 16 *)
(* Tests *)
let () =
(* Add tests *)
print_endline "โ OCaml tests passed"