Recently, I’m doing a lot of work that involves type level magic. And several times already, I needed to express the notion of an optional type. Sure, you could just use () as NoneType, but that doesn’t always work and is not really nice.
So since I needed that a few times already, I would like to use a crate for it. Is there a crate offering this? If not, I would write my own. (Of course, the crate could offer more than those two lines of code. I’m sure one could write many of Option<T>'s methods on type level.)
I tried to search via crates.io and Google, but it’s… not easy since “option” is such a common word around Rust.
In my crates I use Foo<OptionalType = ()>. IMHO () is good enough for this, especially that non-optional usage is Foo<SomeType> rather than Foo<WrapperType<SomeType>>.
This example is a bit stupid, because it doesn’t serve a purpose. But it sometimes does make sense to have optional type parameters.
Mh… I’d disagree, I think. If you use the same arguments for values, it doesn’t sound convincing at all:
-1 is good enough for this, especially that non-optional usage is foo(27) rather than foo(Some(27)).
And yes, sometimes I really want to distinguish between () and no type. I can’t just always use some type as “none value”. Just as we can’t just use -1 or some value as “none value” for integers.
I don’t think comparison to int is accurate here, because int values are a closed set, but types are an open set.
If you require T: Trait then due to orphan rules your implementation on () will be distinct from all other implementations, since users will have to use their types or newtypes.
Fair point about ints, but String is also an infinite set. And I think you’d agree that None and Some("") are semantically different things in many situations.
And again, maybe I really want to use all possible types within SomeType. And want to distinguish each of them from the situation where no type is available. Just as I would say “No, I don’t want to use one specific string value to denote the absence of a string value”. (A small example where I can’t use specific types to denote the absence of a type).
So yeah, I think the question is not whether I need it, but simply if there is already a crate offering this.
So you want an uninhabited type basically? Because your NoneType and () are essentially the same thing - a ZST but inhabited. ! would give you a true uninhabited type, but it’s unclear when/if that will land in stable (it was shelved recently).
You can roll your own with an empty enum, but be mindful that you can trigger UB if you attempt to form references to such a thing (via unsafe, or whatnot).
Whether or not the type is inhabited is not that important to me (at least not right now). Again, I'm not talking about values at all. And since the property "inhabited" is only relevant when talking about values of a type... it's not relevant.
Well, at runtime they sure are. But I'm not interested in runtime behavior, as already explained. And at type level, they are different types.
I feel like I fail to make myself understood
Imagine that you want to write a function foo that takes no value parameter, only a type parameter (similar to mem::size_of). Now the function is supposed to do different things for different types.
foo::<SomeType<i32>>() is supposed to print "yeah! i32 :)"
foo::<SomeType<()>>() is supposed to print "yeah! () :)"
foo<SomeType<!>>() is supposed to print "yeah! ! :)"
Ok, I understand what you’re getting at. I’m personally not aware of such a crate. I’ve seen quite a bit of code using () internally to represent what you’re after, and then they implement whatever trait(s) for it that makes it work with the rest of their code. Perhaps there is such a crate, but I suspect it’s likely not very popular because just defining NoneType and SomeType<T>(pub T) doesn’t really buy you much. It’s the implementations of various usecase-specific traits for them that allows them to be plugged in semantically. And those traits and requirements will vary from project to project.