I'm working on a command line tool to process my data. The workflow is easy, the program:
- loads some files and creates structs, that are stored in a Vec<_>
- there are some filters, that define which struct to process
- there are also actions, that are to be applied to structs selected with filters
Both filters and actions are controlled by command-line options.
So far I implemented that in a straightforward way, say:
if args.if_condition_1 {
filtered = input.iter().filter(|&s| s.chars().next().unwrap().is_uppercase()).collect();
}
if args.if_condition_2 {
// other filter here
}
// ... than a similar block for mapping
but the more filters and actions I implement, the more cluttered is my code. Ideally, I'd like to store my filters and actions in closures. In Python that would look like:
input = ["Zebra", "nice camel", "horse"]
filter = lambda s: True # Default filter is always True
# Process command line flags to see what filter to apply
if args.is_uppercase:
filter = lambda s: s[0].isupper()
action = lambda s: s # Default action makes nothing
# Process command line flags to see what action we do this time
if args.make_uppercase:
action = lambda s: s.upper()
for s in input:
if filter(s):
print(action(s))
I've tried to use closures, e.g.:
let mut filter = |s: &String| { true };
if args.is_uppercase { filter = |s: &String| { s.chars().next().unwrap().is_uppercase() }; }
, but I've only learned that:
6 | if true { filter = |s: &String| { s.chars().next().unwrap().is_uppercase() }; }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
|
= note: expected closure `[closure@filter_action.rs:4:22: 4:34]`
found closure `[closure@filter_action.rs:6:24: 6:36]`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
Can this be achieved with closures or do I have to implement a hierarchy of objects of some trait?