So, as the perpertrator of this, sorry for taking a while to respond. Luckily though, if I put off arguing for sanity long enough @glaebhoerl will usually step in and do it for me. So Iâm not sure I have anything more to add, but Iâll tryâŠ
First though,
I do think in retrospect we didnât spend enough energy evaluating the impact of this change. Itâd be good to have phased it in more gently, at minimum by contacting crate owners. I think this is my fault as the reviewer of the PR for not double-checking that we had run crater and so forth.
This is also my fault for complaining and rushing you and not reminding you that we probably need to do a crater run first. So sorry for that.
I agree this is prudent. There doesnât seem much reason to rush this particular feature.
We should try and ease these changes in rather than throwing them at people, sure, but keep in mind that every month we drag our feet before making a breaking change to the language more code gets written that will eventually break. The bug for this had been open since Feb 2014. So I think another lesson to learn from this is to pay more attention to language inconsistencies and put a high priority on fixing them sooner rather than later. Cool new features and fulfilling the communityâs wishlist is great and all but we shouldnât leave backwards-incompatibility landmines sitting around for three years.
interestingly, we came to some conclusions that seem somewhat at odds with the current exhaustiveness checking changes.
I wasnât under the impression that we ever really reached a conclusion there. Or at least it didnât feel that way to me. But it was stupid of me to implement this the only way that made sense to me rather than try to finish the discussion. So Iâm sorry for that aswell.
Speaking of making sense thoughâŠ
Why would we ever want to say that &Void or &! are not uninhabited. Iâm still not getting it. The whole point of reference types (as opposed to raw pointers) is that they always point to valid data. The fact that &T and T have the same cardinality is so fundamental to Rust that itâs baked into the syntax. This is why we can write things like match bool_ref { &true => ..., &false => ... } without having an extra wildcard pattern. match void_ref {} is exactly the same thing. I get that the optimizer may want to treat &T as *const T but the typechecker doesnât, even in unsafe code.
The argument in favor of making &! inhabited seems to be that, at least in unsafe code, we donât want to assume that references really give the guarantees they say do or else we might break things by over-optimizing. But thereâs an important difference here between &bool and &!: Given a &bool in an unsafe block we canât assume that itâs valid. But given an &! in an unsafe block we can assume that itâs invalid. This kind of assumption is what allows us to say that a function which returns ! is diverging and a patttern match on ! is unfullfilable, rather than saying that we donât know âwhichâ ! we have and so we shouldnât assume that code with a ! in scope is dead code. The same exact same logic applies to &!, Void and &Void - theyâre statically impossible, ergo their patterns are unfulfillable. In the case of a &bool in an unsafe context we donât really know what black magic the programmer has done around it so we need to be conservative to avoid breaking things. In the case of &! we know for a fact that the code is already dead or broken. Is there a subtly to this Iâm not seeing?
I think we should consider trying to restore the old behavior around empty enums temporarily
The thing is - other than fixing those bugs - I canât see how we can roll this out any more painlessly. If we stick it behind a feature gate then, eventually, the feature will gate open and people will get the same warnings theyâre getting now. Is there any reason for rolling back or gating the PR other than some confusion around whether &Void is uninhabited?