The signature of
Option<T>::map_or_else is as follows:
fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, def: D, f: F) -> U
This is highly unintuitive as the two closure parameters are in the reverse order of that suggested by the function’s name. This means that anyone who tries to invoke it from memory, including myself earlier today, is going to run into a compiler error on the first try.
let first_word_letter_count = "hello world".words().next().map_or_else( |word| word.len(), || 0 );
The above makes sense, logically. If the value is there, map it and return the result. Otherwise (implying a secondary operation), call a closure which will produce a substitute value. Even the documentation supports this logical progression:
Applies a function to the contained value or computes a default.
However, trying to invoke the method this way will result in a compiler error because the no-arg closure is required to be first. The particular reasoning behind this design is not given, but I have found precedent in Haskell’s
maybe :: b -> (a -> b) -> Maybe a -> b
However, I don’t think Haskell’s intuition for parameter ordering can extend to Rust because Rust doesn’t have currying, partial application, or (global) lazy evaluation. And even in Haskell the ordering isn’t necessarily intuitive.
I am aware that this method and
Option itself have both been marked as
Stable. However, I believe minor unintuities like this add up and ultimately reflect poorly on the user experience of the language as a whole.