After reading nightly documentation for the never type I started wondering why ! does not implement the Iterator trait.
The documentation states:
In general, if a trait only has methods taking a self parameter (or &self, or an argument of type Self, etc.), consider implementing it for !
Iterator trait meets this criterion. There is also an associated type, but the Not trait has an associated type as well and ! implements Not. In both cases ! seems like the most obvious choice for the item type.
I tried implementing Iterator for an uninhabited enum and it looks like it should be possible for ! as well.
enum Void {}
impl Iterator for Void {
type Item = Void;
fn next(&mut self) -> Option<Self::Item> {
match *self {}
// could also be None, but it does not matter, right?
}
}
fn void_panic() -> Void {
panic!()
}
fn my_foo() -> impl Iterator<Item=u32> {
void_panic()
// error[E0271]: expected `Void` to be an iterator that yields `u32`, but it yields `Void`
// instead of
// error[E0277]: `Void` is not an iterator
}
fn main() {
my_foo();
for x in void_panic() {
// warning: unreachable expression
// instead of
// error[E0277]: `Void` is not an iterator
}
}
Am I missing some potential problem? Is there a reason why ! does not implement Iterator
The problem is that one could implement it only for one single Item type, so which one to pick?
IMHO, what could possibly be more sensible for this use case is for impl Trait[1] return types in functions that diverge to create some fresh uninhabited !-like anonymous types on the fly which then implement the required trait bound; maybe even just as a fallback.
at least for traits like Iterator where every method has a self argument that makes them uncallable for uninhabited Self types ↩︎
Or, I have no idea how hard this would be to implement, but if we make !: Iterator<!>, then extend fallback such that impl Iterator<!> can coerce to an iterator over any type. That would solve this problem of which item type to use.
Oh, I think I understand the problem now. In other words, it would make sense for ! to implement Iterator for all possible Item types, not just !?
If so, doesn't the same logic applies to std::ops::Not? If would make sense for ! to implement Not for all possible associated Output types. It is just less problematic in practice as it is unlikely to see a function returning impl Not<Output=_some_type_>.
In other words, a lot of code requires impl Iterator<Item = SomeSpecificT>, so for the implementation of Iterator for ! to be useful it would have to support all those cases.
However it does not make sense because <! as Iterator>::Item is a single type, so which one is it?
Even if you make ! coercible to something else that implements Iterator<Item = SomeSpecificT>` it still won't work because coercions are not attempted when a generic type is expected.
Yes, the implementation of Not for ! is not ideal and has the same issues as an hypothetic implementation of Iterator for !
I think the problem is that Rust does not have type hierarchy[1], and thus trait bounds cannot define covariance and contravariance. Because of that ! cannot be a "true" Bottom Type like full OOP languages can have - it just have lots of special-casing to make it behave like one.
Traits and lifetimes have hierarchies - but not types. ↩︎
I wonder why did we make Item an associated type instead of a type parameter of trait Iterator? One possible reason I can think of is making .collect() works better, but are there any other reasons?
Intuitively it makes sense that if something can be iterated over it will yield one type of items, while if it were a generic it would be kinda confusing that you can iterate in a way and get one type of items or iterate in another way and get a different type of items.