-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Experiment] Add never
as a type alias for !
#79707
[Experiment] Add never
as a type alias for !
#79707
Conversation
r? @dtolnay (rust-highfive has picked a reviewer for you, use r? to override) |
|
I disagree with the premise that fn foo() -> ! { ... } // Does NOT return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would prefer not to add this. Whether or not it's also given a name under std::primitive, it remains valuable to make !
work as a type because it already partially does in stable Rust, such as in let _: fn() -> ! = std::process::abort;
. As for whether to expose never
as a synonym for it, to me it's not a strong enough motivation if it's a symbol that appears for other purposes in the grammar, or if googling is hard. These are factors that are taking into account by the language design team as a tradeoff against many other things. Other languages land at a different balance of keywords vs punctuation (ALGOL with begin
end
instead of {
}
; comment
instead of //
) but there is still value in syntax for the sufficiently special things.
I'll close the PR but mention @nikomatsakis, the never type stabilization lead according to rust-lang/lang-team#60, in case they see value in reopening and putting this through lang team fcp.
This effectively renames the never-type
!
tonever
, but should be fully backwards compatible.This is an idea I had and I just tried it out. It was surprisingly simple to implement. Example:
Motivation
The never-type
!
is an essential feature in Rust to make type inference in eg.match
work:Here
x
can be inferred to have the typeu32
, because the other two match arms have the type!
, meaning that they never yield a value that would need to be stored inx
. So in some sense,!
is a primitive type that marks control flow.Currently though, you never need to write the
!
, besides the return type of a diverging function. This makes it an "niche" or "advanced" language feature. With the stabilization of the never-type this might change though.The syntax of using the
!
symbol is confusing though, when you first stumble on it as someone learning Rust. Any intuition for what the!
symbol might mean is misleading, since it's already used with macros, top-level attributes and doc comments in Rust. This is made worse by the fact that you can't even google it. The!
symbol is just filtered out of any search query, and I even get amusingly unrelated result like hotel bookings when searching for "Rust !".Solution Idea
To fix that I had the idea introduce a primitive type whose name is what we have been calling this feature since 2016:
never
.Actually, it is just a type alias
pub type never = !
incore::primitive
that is exported in the prelude.This should make it fully backwards compatible, since you can use
never
and!
interchangeably, and the new prelude export is shadowed if somebody made their own type callednever
.I changed some return types of core and std functions, and some never-type tests to verify that this proof-of-concept works. To my own surprise, bootstrapping works and all UI tests pass. There was one weird interaction with a stability attribute and one case where I had to use the full path
crate::primitive::never
, indicating that there are issues in some corner cases, but I haven't investigated further.Downsides
The biggest downside is that existing stable Rust code should be migrated to
never
, since there shouldn't be two ways to express exactly the same. An edition migration would be a good fit: Warn on 2018 and below, error on 2021. But even if the change is straightforward and probably easy to implement in rustfix, the ecosystem impact should be carefully considered.I'd argue it's a relatively small cost, but also only a small gain.
Apparently the Syntax for diverging functions has always been low-priority, so it was overlooked in the rush to Rust 1.0, and again ignored in 2018. Even in the long discussion of the never type RFC the syntax was barely mentioned. Though it's said where it comes from ("Yes, it was a bottom type, that's what
bot
and the bang sign refer to (!
is supposed to look like⊥
)") and it was decided to call the featurenever
.Another issue is that it becomes bad practice to name a variable
never
. You can do it, just like you can name a variableu32
orchar
, but it will look ugly when syntax highlighters color it like the other primitives. This will impact existing code that usesnever
as a name. I guess it won't be a problem for types, other than someone defining their ownenum never {}
type.Also, diagnostics would need changes. They mention
!
in type errors whennever
is used.What now?
First of all, I'm curious what other people think.
Does this make sense?
Is it to late to make such a change to the never-type?
Is it worth the downsides?
If there is interest, should I write a more detailed proposal?