I decided to take a look closer into this to gauge how useful it would be. Looking just at Option<T>, we have the following eager/lazy pairs:
-
unwrap_or/unwrap_or_else
-
map_or/map_or_else
-
ok_or/ok_or_else
-
and/and_then
-
or/or_else
-
get_or_insert/get_or_insert_with
Of special note is the map_or pair, whose signatures are (approximately)
fn map_or<U>(
self,
default: U,
f: impl FnOnce(T) -> U
) -> Option<U>
fn map_or_else<U>(
self,
default: impl FnOnce() -> U,
f: impl FnOnce(T) -> U
) -> Option<U>
The “lazy” paramter is the middle one; thus, #[lazy_alt] should have to look like
#[lazy_alt(alt = "map_or_else", arg = "default")]
fn map_or<U>(
self,
default: U,
f: impl FnOnce(T) -> U
) -> Option<U>
I think it’s also worth hammering out what #[lazy_alt] (and dually #[eager_alt], it it’s even necessary) should look like:
- Takes two parameters, the name of another function and the name of one of the attributee’s parameters.
- The
alt must be in the same module, with the same visibility, as the attributee. If the attributee is inherent, then alt is an inherent function of the same type with the same self type. If the attributee is a trait function, alt must be in the same trait with the same self type.
-
alt must have the same signature as the attributee, except for arg, which must be of type impl FnOnce() -> T, where T is the “eager” type of arg.
This should be enough information for the compiler to raise a lint warning if it sees the eager alternative given something that isn’t const.
Unresolved question: do we want to be able to give a function multiple lazy alternatives, for different parameters? (My thought is not because of combinatorial explosion, but I want to hear what other people think.)