In my experiments with generics I dug myself into a hole where I need to provide a type for a throwaway impl FnOnce which is never called.
More specifically, I need to instantiate Either::Right where Either:Left holds FnOnce(()).
use either::*;
// How to provide impl FnOnce without a closure?
fn make_right<R>(r: R) -> Either<impl FnOnce(())->R,R> {
Right(r)
}
My current solution is cumbersome and I suspect there is a better way:
use either::*;
// Let it infer FnOnce from dummy closure, then set Right value
fn make_right<R>(r: R) -> Either<impl FnOnce(())->R,R> {
let dummy = |()| -> R { unreachable!() };
let mut x = Left(dummy);
x = Right(r);
x
}
You can use any concrete type that implements FnOnce(()) -> R. That includes closures with such a signature, but also plain function pointers. So you can make your make_right function a lot easier by using fn(()) -> R instead of inferring the type from a dummy closure:
Maybe my favorite approach I found for such cases is to use uninhabited closure types. Slightly tedious, but some neatly optimal layout at run-time; the Either enum would become essentially (in practice; not a compiler guarantee) a transparent wrapper in this case, as you can see by how the size of the Either is still the same as the u32 itself.
use either::*;
// How to provide impl FnOnce without a closure?
fn make_right<R>(r: R) -> Either<impl FnOnce(())->R,R> {
#[allow(unreachable_code)]
if false {
let i: std::convert::Infallible = unreachable!();
return Left(move |_| {
let _ = &i; // actually capture i in edition 2021
match i {}
});
}
Right(r)
}
fn main() {
dbg!(std::mem::size_of_val(&1_u32));
dbg!(std::mem::size_of_val(&make_right(1_u32)));
}
Yes. Even though it (unfortunately) looks a lot like function items, the typefn(…) -> … is actually the type for function pointers, so it’s a single concrete type (and not the type of actual functions in Rust [those have individual anonymous zero-sized types], though functions [and non-capturing closures] can be implicitly converted into function pointers).
Fascinating, is it what unstable ! for, but without any nightly features? Why does your capture trick work, but something like Right::<fn(Infallible) -> R, R>(r) doesn't?