From what I understand, it should be a unit struct, the unit itself () would be most clean, but a new unit struct, like for example:
could also work.
The only advantage of Option<Infallible> that I can think of is that it implements Try, but I don't see how that could be important, especially since the type in the Error variant in Result doesn't have to do that.
Result actually does that as well, with <Result<T, E>::Residual being <Result<Infallible, E>>.
@scottmcm will no doubt chime in, but in the meantime the "Note to implementors" of Try::Residual discusses this:
The choice of this type is critical to interconversion. Unlike the Output type, which will often be a raw generic type, this type is typically a newtype of some sort to "color" the type so that it's distinguishable from the residuals of other types
This is why Result<T, E>::Residual is not E, but Result<Infallible, E>. That way it's distinct from ControlFlow<E>::Residual, for example, and thus ? on ControlFlow cannot be used in a method returning Result.
If you're making a generic type Foo that implements Try<Output = T>, then typically you can use Foo<std::convert::Infallible> as its Residual type: that type will have a "hole" in the correct place, and will maintain the "foo-ness" of the residual so other types need to opt-in to interconversion.
So the Residual has to be a bespoke type to make conversions work. If I understand correctly, struct OptionResidual would also work, but Option<Infallible> (hopefully spelled Option<!> in the future) is already there, and in a way the natural, "correct" type to answer the question "What is left when you subtract T from Option<T>?" (If enum variants were types, then I guess the residual could simply be the None type.)
that's conveniently constructible from the token None.
And thus it serves as a unit struct in essentially all the ways that matter.
Could it be a separate type? Certainly. But then we'd have to argue about bikeshed its name, whether it should have any methods, which traits it should implement, etc. That might be worth doing if this were something that people would need to mention frequently, but when it's a usually-hidden detail, it's not clear that it'd be helpful.
Using ! is then just a convenient yet efficient way to create those residual types. It's nice as a user, too, not to need to understand an additional type. Just the same "it can't be that one" pattern that's also used in TryFrom, where for example i32::try_from(10_u8) gives a Result<i32, !>, since it's a widening conversion which cannot fail. Note that there's nothing special going on with ! here -- any uninhabited enum would work fine.
I think the point is that the residual for Option<T> would be Option<T>::None; the residual for Result<T, E>Result<T, E>::Err.
This works (assuming the existence of enum variants as types) but isn't really a great solution for a couple reasons:
While it works for 2-variant enums, it doesn't work well for 3-variant enums pulling one variant as the continue case, 2 as the break, since it's a separate feature to have a type which is one of multiple variants. Even with e.g. pattern types, current drafts of what the feature would look like have pattern types behave similarly to subtypes of the full type.
The current drafts for enum variants as types pretty much all agree that they should be the same size as the full enum, just with the other variants made illegal. Option<!> is basically for<T> Option<T>::None except without the likely desirable subtype relationship.
In a -> Option<String> function, you want ? to all hit the same Option<String>: FromResidual<Option<!>>. If Option<T>'s residual was Option<T>::None, then you'd end up with it potentially needing to monomorphize Option<String>: FromResidual<Option<i32>::None> and Option<String>: FromResidual<Option<bool>::None> and …
Honestly the biggest problem is that the name Infallible only makes sense in the context of the Result error type. If it was the Typescript name Never then Option<Never> reads pretty clearly as what the meaning is (to me)
The goal is to stabilize ! and make Infallible just a type alias for it at the same time. At which point these error messages will be Option<Never> or Option<!>, which will hopefully be easier to read.
EDIT: wow, it's my 6th anniversary of joining IRLO, says the little pie icon.
Option<Infallible> guarantees that the Some variant will never be constructed; even if that doesn’t really matter for this sort of autogenerated code, it also means the whole enum becomes a zero-sized type (because there is only one possible state). Of course, it’d be optimized away completely in a release build, but even so using Infallible is better on both theoretical and practical grounds.
Depends on how the hypothetical Option<()>::None subtype would be implemented. I would actually prefer if it had the same size and layout as the original Option<()> so yea, in that case, it wouldn't be zero sized like Option<Infallible> is.
And it being a ZST is helpful because the desugaring involves ControlFlow<Residual, Output>. And if the output is NonZeroU32, then that ControlFlow optimizes down to being just an i32 (not guaranteed) when the residual is a ZST, simplifying the intermediate values. (For example in NonZeroU32::new(x)?.)