// Example 122: Higher-Order Functions with Lifetime Constraints
//
// When HOFs deal with references, lifetimes must be explicit
// to tell the compiler how long returned references live.
// Approach 1: HOF returning a reference โ needs lifetime
fn find_first<'a, F>(items: &'a [&'a str], pred: F) -> Option<&'a str>
where
F: Fn(&str) -> bool,
{
items.iter().copied().find(|&s| pred(s))
}
fn approach1() {
let data = vec!["apple", "banana", "cherry", "date"];
let long = find_first(&data, |s| s.len() > 5);
assert_eq!(long, Some("banana"));
println!("First long: {}", long.unwrap());
}
// Approach 2: Function composition
fn compose<A, B, C, F, G>(f: F, g: G) -> impl Fn(A) -> C
where
F: Fn(B) -> C,
G: Fn(A) -> B,
{
move |x| f(g(x))
}
fn pipe<A, B, F: Fn(A) -> B>(x: A, f: F) -> B {
f(x)
}
fn approach2() {
let double = |x: i32| x * 2;
let add1 = |x: i32| x + 1;
let double_then_add = compose(add1, double);
assert_eq!(double_then_add(5), 11);
let result = pipe(pipe(5, double), add1);
assert_eq!(result, 11);
println!("compose(add1, double)(5) = {}", double_then_add(5));
}
// Approach 3: HOF on borrowed slices with lifetime propagation
fn transform_all<'a, F>(items: &[&'a str], f: F) -> Vec<String>
where
F: Fn(&str) -> String,
{
items.iter().map(|&s| f(s)).collect()
}
// HOF that returns references from the input
fn filter_refs<'a, F>(items: &'a [String], pred: F) -> Vec<&'a str>
where
F: Fn(&str) -> bool,
{
items.iter()
.filter(|s| pred(s))
.map(|s| s.as_str())
.collect()
}
fn approach3() {
let words = ["hello", "WORLD", "Rust"];
let lower = transform_all(&words, |s| s.to_lowercase());
assert_eq!(lower, vec!["hello", "world", "rust"]);
let owned = vec!["hello".into(), "WORLD".into(), "Rust".into()];
let filtered = filter_refs(&owned, |s| s.chars().next().map_or(false, |c| c.is_uppercase()));
assert_eq!(filtered, vec!["WORLD", "Rust"]);
println!("Lower: {:?}", lower);
println!("Uppercase starts: {:?}", filtered);
}
fn main() {
println!("=== Approach 1: Find with Predicate ===");
approach1();
println!("\n=== Approach 2: Composition ===");
approach2();
println!("\n=== Approach 3: Transform Borrowed Data ===");
approach3();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_find_first() {
let data = vec!["a", "bb", "ccc"];
assert_eq!(find_first(&data, |s| s.len() >= 2), Some("bb"));
assert_eq!(find_first(&data, |s| s.len() >= 5), None);
}
#[test]
fn test_compose() {
let f = compose(|x: i32| x.to_string(), |x: i32| x * 2);
assert_eq!(f(5), "10");
}
#[test]
fn test_pipe() {
assert_eq!(pipe(5, |x| x * 3), 15);
}
#[test]
fn test_transform_all() {
let items = ["abc", "DEF"];
let result = transform_all(&items, |s| s.to_uppercase());
assert_eq!(result, vec!["ABC", "DEF"]);
}
#[test]
fn test_filter_refs_lifetime() {
let owned = vec!["hello".to_string(), "world".to_string()];
let refs = filter_refs(&owned, |s| s.starts_with('h'));
assert_eq!(refs, vec!["hello"]);
}
}
(* Example 122: Higher-Order Functions with Lifetime Constraints *)
(* OCaml HOFs work without worrying about lifetimes.
Rust HOFs that take/return references need lifetime annotations. *)
(* Approach 1: Function that takes a predicate and returns matching slice *)
let find_first pred lst =
try Some (List.find pred lst)
with Not_found -> None
let approach1 () =
let data = ["apple"; "banana"; "cherry"; "date"] in
let long = find_first (fun s -> String.length s > 5) data in
assert (long = Some "banana");
Printf.printf "First long: %s\n" (Option.get long)
(* Approach 2: Composing functions *)
let compose f g x = f (g x)
let pipe x f = f x
let approach2 () =
let double = ( * ) 2 in
let add1 = ( + ) 1 in
let double_then_add = compose add1 double in
assert (double_then_add 5 = 11);
let result = pipe 5 double |> add1 in
assert (result = 11);
Printf.printf "compose(add1, double)(5) = %d\n" (double_then_add 5)
(* Approach 3: Applying transformations to borrowed data *)
let transform_all f items =
List.map f items
let approach3 () =
let words = ["hello"; "WORLD"; "Rust"] in
let lower = transform_all String.lowercase_ascii words in
assert (lower = ["hello"; "world"; "rust"]);
Printf.printf "Lowered: %s\n" (String.concat ", " lower)
let () =
approach1 ();
approach2 ();
approach3 ();
Printf.printf "โ All tests passed\n"