Fallible unpacking can let you avoid filtering
If you have an iterable input, e.g. command-line arguments, where some values are known to be bad (the program name is not useful), and others can be split into multiple discrete values, the normal solution to loop over .skip(n)
to bypass the bad values (e.g. .skip(1)
to skip the program name in the arguments), split
/collect
the rest to a Vec
, and use the values by indexing (unless they're referred to many times, it's not worth the cost to unpack). For example, imagine a program that receives four numbers at a time, comma-separated as command-line arguments, and must compute a value using each value individually. The simple solution (using the flat_map
trick to avoid a level of unwrap
ing or use of filter_map
) is:
fn main(){
for a in std::env::args().skip(1){
let v:Vec<u32>=a.split(',').flat_map(str::parse).collect();
println!("{},{}",v[0]+v[1],v[2]+v[3])
}
}
And that's not bad. But the .skip(1)
costs eight characters, and all the indexing costs 12 more, for a total of 20 characters we'd like to avoid. By using if let
with slice-unpacking, we can imply the skip (assuming the program name isn't four numbers separated by embedded commas), and unpack to individual names, while still saving characters:
fn main(){
for a in std::env::args(){
let v:Vec<u32>=a.split(',').flat_map(str::parse).collect();
if let[m,n,o,p]=a.split(',').flat_map(str::parse).collect::<Vec<u32>>()[..]{
println!("{},{}",m+n,o+p)
}
}
}
The difference here is pretty trivial (the second version shaves 8+12=20 characters by avoiding .skip(1)
and indexing four times, but it costs 18 more to wrap in if let
, use a turbofish on collect
instead of letting type-inference handle it the pattern itself is longer (let[m,n,o,p]
vs. let v
), and [..]
is needed to convert from Vec
to slice for the pattern matching.
But:
- Unpacking more values only costs two characters more per name, and saves three characters the first time said value is used, and if you need to use any given value more than once, it saves three more characters per use, etc.
- Frequently, type-inference can figure out the type of the matched values, and that will allow you to replace
iterator.collect::<Vec<u32>>()
with Vec::from_iter(iterator)
, saving eight characters and making it much quicker to reach the break even point.
If the iterator had no invalid values to skip, this wouldn't be worth as much (the .skip(1)
is 40% of the savings here), but the general pattern can be useful even then if you have enough things to unpack, and use them often enough.
Try both out on the Rust Playground