- Feature Name: raw_identifiers
- Start Date: 2017-07-07
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)
Summary
Add a raw identifier format r#catch, so crates written in future language epochs/versions can still use an older API that overlaps with new keywords.
Motivation
One of the primary examples of breaking changes in the epoch proposal is to add new keywords, and specifically catch is the first candidate. However, since that’s seeking crate compatibility across epochs, this would leave a crate in a newer epoch unable to use catch identifiers in the API of a crate in an older epoch.
A raw syntax that’s always an identifier would allow these to remain compatible.
Detailed design
I propose r#catch as a syntax which always parses as a catch identifier, ignoring any possible keywords. Specifically, r# is the beginning of an escaped identifier token, followed by any legal identifer sequence. The r# is not kept as part of the resulting identifier in any way.
How We Teach This
We can call these simply “raw identifiers”. It may also be called stropping, but this term is not very common.
Using r#catch is visually similar to the existing r#"raw "escaped" string"# syntax, which will hopefully give an intuitive clue to advanced users what is going on.
This feature will most likely only be seen in rare corner cases. It may only need to be taught at all in documentation for a breaking change which is introducing a new keyword.
Documentation updates:
-
The Rust Programming Language Appendix A “Keywords” should mention the
r# syntax.
- Rust Reference 2.2 “Identifiers” should specify the
r# syntax.
- Perhaps also Grammar section 3.2.1 “Identifiers” and 3.5.1 “Keywords”.
Drawbacks
New syntax is always scary/noisy/etc.
It might not be intuitively “raw” to a user coming upon this the first time.
Alternatives
-
The exact syntax is a huge bikeshed.
- C# uses
@class, which might work for Rust, although it collides with boxed pointer of yore. It might be also be ambiguous with pattern @ bindings.
- Other escapes are possible, like
\catch.
- We could allow multiple
# and terminate them to allow even non-identifier characters
let r##The #1 weirdest syntax!## = this;
-
The status quo: epochs with new keywords may not be able to use those in the API of prior epochs.
- There may be contextual mitigations. In the case of
catch, it couldn’t be a fully contextual keyword because catch { ... } could be a struct literal. That context might be worked around with a path, like old_epoch::catch { ... } to use an identifier instead. Contexts that don’t make sense for a catch expression can just be identifiers, like foo.catch(). This might not be possible for all future keywords.
-
There might also be a need for raw keywords in the other direction, e.g. so the older epoch can still use the new catch functionality somehow. I think this particular case is already served well enough by do catch { ... }, if we choose to stabilize it that way.
Unresolved questions
- Do macros need any special care with such identifier tokens?
- Should diagnostics use the
r# syntax when printing identifiers that overlap keywords?
- Does
rustdoc need to use the r# syntax? e.g. to document pub use old_epoch::*