Pre-RFC: Unreserve inheritance-related keywords

What about introducing "raw keywords" syntax? E.g., async can also be written as k#async, foo.await as foo.k#await and so on. This way, new keyword can be easily introduced in raw form without breakage (e.g. k#foo is keyword, r#foo and foo are identifiers). And in the next edition default behavior changes: foo is parsed as keyword.

1 Like

k#foo is already 3 distinct tokens, k # foo, which matters for macro_rules! compatibility. We were able to use r#foo for raw identifiers only because r# was already greedily tokenized as the start of a raw string like r#"foo"#, and gave an error otherwise. One possibility I mentioned in that RFC 2151 was re-using the same from raw byte strings as a raw keyword, like br#foo.

There's also this open RFC for experimental keywords:

3 Likes

For the record, this is the complete unused keywords list in the praser:

        // Keywords that are used in unstable Rust or reserved for future use.
        Abstract:           "abstract",
        Become:             "become",
        Box:                "box",
        Do:                 "do",
        Final:              "final",
        Macro:              "macro",
        Override:           "override",
        Priv:               "priv",
        Typeof:             "typeof",
        Unsized:            "unsized",
        Virtual:            "virtual",
        Yield:              "yield",
1 Like

box and yield are used in nightly

1 Like

I searched the rust codebase for usages of these keywords using the command rg "abstract " -g '!llvm-project/*' -g '*.rs' | rg --invert-match "//" and manually filtered out occurences in strings or as parts of identifiers, to find which keywords are used as part of unstable features.

Actually used keywords:

  • box: used with #![feature(box_syntax)] about 1000 times and overall 3081 matches.
  • macro: used with #![feature(decl_macro)] about 100 times and overall 7045 matches.
  • yield: used as part of async generators about 100 times and overall 552 matches.

Unused keywords.
I also counted how often they appear as part of strings and identifiers, to give some numbers for arguments regarding usefulness. (eg. rg "abstract" -g '!llvm-project/*' -g '*.rs' | rg --invert-match "//" --count)

  • abstract: 5
  • become: 42. This one was reserved for use in tail-call-optimasations.
  • do: 11113. This word is essential in the english language.
  • final: 239
  • override: 347
  • priv: 910
  • typeof: 7
  • unsized: 317
  • virtual: 141
1 Like

I am strongly against unreserving any currently reserved keywords. Not only does it add to churn, it reduces the number of keywords available to rust should those features become something we want to add in the future. This wouldn't be an issue if rust's keywords used their own namespace, like in the RFC that @cuviper mentioned earlier, but as it stands rust has a limited set of keywords that it can practically use.

Put another way, right now abstract isn't in use as a keyword, but if it is unreserved and then needed in the future, it won't be available. So, unless we also decide on a namespace reserved for keywords, I'm against this proposal.

1 Like

Keywords should make sense in the contexts they are used in. As an example, imagine that Rust needed a new keyword for delegation. None of the reserved keywords would make much sense in that context!

This set can be extended if and when necessary in a new edition.

Agreed. My assumption is that current set of reserved words may be needed as keywords in the future, and that they would make sense in the context that they were used in.

And this is where I disagree. The issue is the extraordinary amount of churn that this can cause. Consider any word that you want to convert from being acceptable as an identifier, to being a full keyword. Every line of code that used that word would need to be converted over, or risk not being able to be compiled under the new edition. cargo fix --edition could be extended to catch this problem, but that only helps with your own code; if you depend on some other code to be upgraded, then you're going to have a headache for a while (I do not want to maintain my own fork of someone else's code! I use other people's code so that I don't have to maintain that code!).

Honestly, if I had to maintain a fork for each and every one of my dependencies, for each and every variable that was suddenly modified into being a keyword I would modify them with the suffix :face_with_symbols_over_mouth: . (abstract_🤬, yield_🤬). Don't make me do that, it'll make my code look unprofessional! :wink:

Alternatively, since rust supports Unicode, we could use some emoji as the keyword prefix (maybe :key: for keyword?) As far as I know, this won't break any common rust code, but it will be really, really weird to have to create a new key mapping so that it's possible to type :key: fast...

Dependencies will keep compiling with their own choice of edition, so new edition keywords shouldn't be a problem in that regard.

6 Likes

The entire point of adding the "editions" mechanism is that surface-level syntax changes to Rust like introducing a new keyword won't have those downsides. AFAIK nobody has ever needed and will never need to fork a crate for edition support (and needing to do so would have been perceived as a colossal failure in the design of editions).

We've already introduced multiple keywords to Rust using this mechanism without anyone having these problems. This would be a reasonable concern to bring up if we were proposing unreserving keywords as part of the editions RFC or immediately after the editions RFC was accepted, but now we have the experience to state unequivocally that it's simply not an issue. Edition-added keywords have proven themselves.


What is true is that the argument for actively unreserving any keywords is relatively weak. The reason I'm weakly in favor of unreserving the "inheritance keywords" is that the other argument, for keeping them reserved, is not merely weak but nonexistent at this point. Every plausible proposal for adding features that are anywhere close to what abstract/virtual/etc mean has actively considered them and decided that brand new keywords like delegate would be a far better fit. All that's left is "Maybe we'll think of a use someday", which is especially unpersuasive given the sheer (over?)discussion of possible future language features we have in this community, the concrete evidence posted above that these words would likely have significant use as regular identifiers, and the fact that we also have the last resort of re-adding a keyword in the one-in-a-million chance we unreserve incorrectly.

Note that this does not apply to the other unused keywords, e.g. become. Those should definitely stay reserved until we've reached some stronger consensus on what to do about their proposed usages, e.g. guaranteed TCO.

5 Likes

And indeed, Rust 2018 added multiple new keywords (including try, even though it had been used as an identifier in the standard library among other places), and the upgrade process still went very smoothly.

1 Like

@cuviper, @lxrec, @mbrubeck, you guys are right, I brain glitched on that. Since crates either specify their edition, or are default assumed to be 2015, there is no ambiguity as to which surface syntax is in use (I should have remembered all that, given it was brought up in another discussion I was a part of...). And with cargo fix --edition being used when upgrading a crate to a new edition, the issue goes away entirely. I retract my argument against this.

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.