Skip to content
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

Conversation

Julian-Wollersberger
Copy link
Contributor

@Julian-Wollersberger Julian-Wollersberger commented Dec 4, 2020

This effectively renames the never-type ! to never, 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:

fn foo() -> never {
    let x: never = panic!();
}

Motivation

The never-type ! is an essential feature in Rust to make type inference in eg. match work:

let x = match some_enum {
    Variant1 => 42u32,
    Variant2 => return true,
    _ => panic!(),
}

Here x can be inferred to have the type u32, because the other two match arms have the type !, meaning that they never yield a value that would need to be stored in x. 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 = ! in core::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 called never.

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 feature never.

Another issue is that it becomes bad practice to name a variable never. You can do it, just like you can name a variable u32 or char, but it will look ugly when syntax highlighters color it like the other primitives. This will impact existing code that uses never as a name. I guess it won't be a problem for types, other than someone defining their own enum never {} type.

Also, diagnostics would need changes. They mention ! in type errors when never 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?

@rust-highfive
Copy link
Collaborator

r? @dtolnay

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Dec 4, 2020
@jyn514 jyn514 added the T-lang Relevant to the language team, which will review and decide on the PR/issue. label Dec 4, 2020
@camelid camelid added the S-experimental Status: Ongoing experiment that does not require reviewing and won't be merged in its current state. label Dec 4, 2020
@memoryruins
Copy link
Contributor

Infallible already exists in stable core/std, https://doc.rust-lang.org/nightly/core/convert/enum.Infallible.html, which is planned to become a type alias for ! after the never type stabilizes. Adding yet another option might make the choice of which to choose arbitrarily unclear for users.

@mark-i-m
Copy link
Member

mark-i-m commented Dec 5, 2020

I disagree with the premise that ! is confusing. I actually found it incredibly clear: ! is not, as in "If you have one, you're not here":

fn foo() -> ! { ... } // Does  NOT return

Copy link
Member

@dtolnay dtolnay left a 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.

@dtolnay dtolnay closed this Dec 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-experimental Status: Ongoing experiment that does not require reviewing and won't be merged in its current state. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants