Functional Transformations with Rust's Fn Traits
This challenge focuses on understanding and utilizing Rust's Fn, FnMut, and FnOnce traits. These traits are fundamental to functional programming in Rust, enabling you to treat functions as first-class citizens and pass them around as arguments or return values. Your task is to implement a series of functions that leverage these traits to perform transformations on data.
Problem Description
You are tasked with creating a module named functional_transformations that provides several utility functions centered around applying transformations to collections of data. These transformations will be defined using closures that conform to the Fn, FnMut, or FnOnce traits. The module should include the following functions:
-
map_fn(data: &[i32], transform: impl Fn(i32) -> i32) -> Vec<i32>: This function takes a slice ofi32integers and a functiontransform(which is anFntrait implementation). It applies thetransformfunction to each element of the slice and returns a newVec<i32>containing the transformed values. -
filter_fn(data: &[i32], predicate: impl FnMut(i32) -> bool) -> Vec<i32>: This function takes a slice ofi32integers and a functionpredicate(which is anFnMuttrait implementation). It filters the slice, keeping only the elements for which thepredicatefunction returnstrue. The filtered elements are returned as a newVec<i32>. -
reduce_fn(data: &[i32], initial_value: i32, accumulator: impl FnOnce(i32, i32) -> i32) -> i32: This function takes a slice ofi32integers, an initial value, and a functionaccumulator(which is anFnOncetrait implementation). It reduces the slice to a single value by repeatedly applying theaccumulatorfunction. Theaccumulatortakes the current accumulated value and the next element from the slice as input and returns the new accumulated value. The final accumulated value is returned.
Examples
Example 1:
Input: data = [1, 2, 3, 4, 5], transform = |x| x * 2
Output: [2, 4, 6, 8, 10]
Explanation: The `transform` function doubles each element of the input slice.
Example 2:
Input: data = [1, 2, 3, 4, 5], predicate = |x| x % 2 == 0
Output: [2, 4]
Explanation: The `predicate` function checks if an element is even. Only even elements are kept.
Example 3:
Input: data = [1, 2, 3, 4, 5], initial_value = 0, accumulator = |acc, x| acc + x
Output: 15
Explanation: The `accumulator` function sums the elements of the slice, starting with the `initial_value`.
Constraints
- The input
dataslice will always contain at least zero elements. - The
transformfunction inmap_fnshould not modify the original data. - The
predicatefunction infilter_fnshould not modify the original data. - The
accumulatorfunction inreduce_fnshould not modify the original data. - All input values will be valid
i32integers. - Performance is not a primary concern for this challenge, but avoid unnecessarily inefficient implementations.
Notes
- Pay close attention to the differences between
Fn,FnMut, andFnOnce.Fnclosures can be called multiple times without capturing any variables by move.FnMutclosures can be called multiple times and may capture variables by mutable reference.FnOnceclosures can only be called once and may capture variables by move or immutable reference. - Consider how the closure's capturing behavior affects which trait it should implement.
- The
reduce_fnfunction should handle the case where the input slice is empty gracefully (returning theinitial_value). - The module should be named
functional_transformations. - The functions should be public.
- Error handling is not required for this challenge. Assume valid inputs.