179. Iterator::max_by_key — Find the Best Element Without a Manual Fold
Finding the “best” item in a collection — longest string, heaviest order, latest timestamp — is one of those tasks where a hand-rolled fold keeps showing up. Iterator::max_by_key does the same job in one call, and min_by_key is right there next to it.
The fold you keep writing
You have a slice of strings and want the longest one. The DIY version looks something like this:
| |
That’s a lot of code for a one-liner concept. max_by_key collapses the whole pattern:
| |
You hand it a closure that extracts the key — the thing you want to maximise — and it returns the item that produced the largest key.
Works on anything Ord
The key doesn’t have to be a number. It just has to implement Ord, which means strings, tuples, dates, your own types — anything totally ordered:
| |
Tuples are where this really shines — you get multi-key sorting for free, because (A, B): Ord compares lexicographically:
| |
Same trick with min_by_key — the entire _by_key family follows the same shape.
The tie-break rule, and why it matters
When two elements produce equal keys, max_by_key returns the last one and min_by_key returns the first. That asymmetry is in the docs and it bites people:
| |
If you need a specific tie-break — “longest word, but earliest in the list when tied” — encode it in the key itself with a tuple, instead of relying on iteration order.
Floats need min_by / max_by
f32 and f64 don’t implement Ord (because NaN), so max_by_key won’t compile if your key is a float. Reach for max_by and pass a comparator instead:
| |
Or wrap your floats in something like ordered_float::OrderedFloat and stay with max_by_key.
Takeaway
Any time you find yourself folding to track the “best so far,” check whether max_by_key or min_by_key says it in one line. The closure extracts the score; the iterator returns the winner. For ties you control the rule by shaping the key.