-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
RFC: Remove the '
from lifetime parameters
#134
Conversation
generic parameter is a lifetime or a type.
I'm in favor. It looks cleaner in all the cases I've considered. |
What about: |
Or (just to add an alternative to the discussion - not to express disapproval) |
IMHO the best way to reduce lifetime noise is to unify variable names and lifetimes:
I haven't thought about this too much, so I'm not sure if it would work outside the context of function signatures, but it seems to work very well there. Combined with dropping the |
I like this for two reasons:
|
+1. I definitely think this will help with the noise aspect (although, I didn't mind the |
we may want syntax for explicit bounds on lifetimes, in order to deal with issues like rust-lang/rust#13703 i.e. something like this might be needed at some point:
That could conflict with this rule from above (from the first bullet): "If the generic parameter is bounded, it is a type parameter." (But maybe that condition is not necessary to preserve, I have not thought too hard about it.) Or maybe we would express that has |
@mcpherrinm that fails for structs that need to take a lifetime as a parameter, but for which you don't have a variable e.g. Edit: stupid github not autocompleting reliably ... stupid people with one letter difference between IRC nick and github nick ... |
@o11c Yeah, it definitely doesn't work in all cases (and may only work in the one specific case I presented). As I said, I haven't thought too hard about making that idea work. I don't think it really affects this RFC meaningfully so it probably doesn't merit further noise in this discussion. |
I don't like this. Lifetimes parameters and type parameters are different and not interchangeable, so they have to differ syntactically. |
|
||
# Drawbacks | ||
|
||
* Syntax highlighters will have a more difficult time determining the difference between lifetimes and type parameters, because lifetimes will no longer constitute a distinct lexical class. |
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 this is a really major concern. Visual distinction of lifetime parameters via syntax coloring is very helpful. You said here that syntax highlighters will have a "more difficult time", but I'm inclined to say that they simply cannot tell the difference as long as they're based on simple parsing rules. Determining the difference will require either using libsyntax
to actually do the parsing (which is rather at odds with the normal goal of highlighting incomplete or buggy code) or reimplementing its logic for determining the role of each parameter. And that's not something most editors are capable of doing (for example, I don't see how Vim could ever highlight a lifetime).
I agree with the motivation. I find the single-quote
I'm assuming that "if it is then followed by a type" is implied? Can we also disambiguate term-level code like An alternative implementation: We could turn our capitalization conventions into hard rules (I think we should do this anyway), and then we can determine whether a parameter is a type parameter or a lifetime parameter by simply observing whether or not it is capitalized. The term level (see example above) would still be tricky. |
I agree with @netvl. I'm inclined to say I don't like this change, and not just for the syntax highlighting reason I already commented about. I like @mcpherrinm's suggestion, which I would have made if he had not done so already, although his use of fn foo(x: &T) -> &'a Y; Or, perhaps more commonly, fn get_mut(&mut self) -> &'self mut T; I think this is less verbose and more readable than And of course this is merely an addition to the current rules, so you can still declare your Besides this, one thing to consider would be if there's a different syntax for lifetime's that's just as concise but doesn't have the issues that |
What about HKT in future: would it be possible to carry lifetime information in borrowed smart pointer types.
I'd probably prefer to stick with & (the anonymous lifetime case any problem solving (syntax highlighting, IDE assists) applied to generics would carry over. I even wondered if 'ints in type system' could work with lifetimes, imagine if there's a way to get the nesting level as an int, could you write expressions on that. |
They do differ syntactically. By case. |
I think this would make lifetimes more confusing, because you'd end up seeing the
Like kballard's suggestion above, this does not handle lifetime-parameterized types well.
There is one. The case convention proposed here. :) For those bringing up syntax highlighting, remember that we don't syntax highlight type parameters either. Yet nobody is proposing a sigil for type parameters, because that would almost certainly be too noisy. This just makes lifetime parameters consistent with type parameters. |
Why not instead create, for each variable, a lifetime of the same name that is the intersection of all lifetimes in the variable type? Then the example just becomes
which I suspect is the way most people mentally think of the example declaration anyway. Regarding the proposal, the thing is that variables and types also don't need to be distinguished, yet they are by snake_case vs PascalCase, and there is no case convention left for single-letter lifetimes (t is a variable and T is a type, there is no third case). Unless we are crazy enough to use greek letters (problem of that is of course that barring copy&paste, a custom keyboard layout is needed to type them efficiently). For the record, it would look like this:
|
I think this would add complexity to the lifetime system.
Types and variables are in separate syntactic positions and separate namespaces. Types and enum variants are both in CamelCase, yet this has rarely been a source of confusion.
This proposal in fact supports that. |
Yes, although it's probably easier to understand a direct "the return value the lifetime of the receiver" than "the return value and the receiver have both lifetime 'a, so 'a will be inferred to be the lifetime of the receiver, so the return value has the lifetime of the receiver" which are the deductions one must mentally make with the current syntax to be able to understand how to use it.
Well, enum variants are types in a natural extension of the Rust type system, which of course is not the case for lifetimes or variables, so it's not quite the same. |
Is this a valid motivation? Are they taking potshots or are they genuinely interested in using Rust? If the latter, has this change been run past them? Is this considerably less egregious to them? It isn't to me, in fact it's worse. The unfortunate thing is you have the same lifetime appear thrice in an otherwise simple signature. Removing the tick makes the lifetime harder to scan past, introduces a naming conflict for the sake of a syntax change but doesn't alter the fact that the lifetime shows up three times. Inferred lifetimes made a lot of this go away and, if I recall correctly, solved this case neatly. But it was agreed that being explicit was better. We hated At the risk of sounding newbie-hostile (quite the opposite in fact), I argue that optimizing for the aesthetics of those new to the language is a dead-end road. You can't write good code until you understand the language sufficiently well and at that point seeing tl;dr - Nothing about the motivation rings true to me. I have a preference for being able to scan/scan past lifetimes easily in signatures. And there are drawbacks. Instead of just being harder to read for people new to the language, I would turn that around and say this proposal makes it easier to read for virtually no-one at all, save those who refuse to read |
But a lexical distinction remains, namely case. So I don't see this as comparable to the changes to remove inference. In practice this will be exactly as explicit as today. |
@pcwalton Case is not a sufficient lexical distinction to use when manually tokenizing code with your eyes. After all, when glancing past |
I don't think it's a big deal as long as the case distinctions are within one nonterminal/namespace. For instance, as I mentioned above, we already use the same case conventions between type parameters and constants and between types and enum variants. (We actually used to use a sigil to separate the latter two, and we removed it in favor of inference, a decision that was heavily resisted at the time but was obviously the correct one in retrospect.) |
Sure, but I'm not arguing any comparison between this change and removal of inferred lifetimes. The point about inferred lifetimes (which I don't think anyone really wants) is that it is the kind of thing needed to satisfy everyone unfamiliar with the language. I still doubt that anyone put off by
Lexical distinction in the form of case is fine for a compiler, but it doesn't pop for me the way a tick does. As much as I don't like the change, my preference isn't my main concern. It's the unknown effectiveness of the change versus the costs listed. But is it even fine for the compiler? Why should this change cause interaction between loop labels and variable names, the explanation being interaction with yet another part of the system? I mean, why has nobody batted an eye at that? I'm not saying we need to name loops and variables the same, but it sounds fragile. (This bit isn't really about the proposed change, except maybe we shouldn't be putting stress on the fragile bits.) |
Honestly, the inferred lifetime thing is really just to reduce typing. I don't think it affects readability in the slightest. |
I presume that the complaint by the Chromium team that you are referring to is https://twitter.com/damienmiller/status/476207923702423552:
Strong +1 on what @mcpherrinm voiced first, that this could be improved by being able to use variable names automatically as lifetimes, which permits what I am sure is the most common case:
As for removing the single quotation mark, I’m ambivalent about that. Overall I don’t think I like it at present, but I’d want to wait longer before deciding that definitely.
Significantly less noisy!? Selection bias, of course, as I already know Rust, but the removal of those three characters makes it harder for me to parse it. Now @mcpherrinm’s suggestion is “significantly less noisy”, and every bit as explicit. At present, lifetime parameters are hidden from types in most error messages. This, incidentally, is a source of enormous pain to people starting out with lifetimes as it renders some of the error messages that they care about most useless, as they complain about type Currently lifetime parameters must come first in type parameter lists. This restriction appears to be removed under this proposal. Closures of the form Labels on loops,
@pcwalton: I can only accept this as a valid argument if case is enforced: that types must be upper-case and lifetimes must be lower-case. Otherwise this distinction is not truly there. For syntax highlighting, I do perceive a technique that will satisfy most cases: if you have an ampersand followed by a word other than Labels can be highlighted by a separate rule with no difficulties (in fact, they can be highlighted as But these things does not satisfy highlighting inside type parameters, nor for closures, nor can it do much for macros. Remember that ease of syntax highlighting is in many ways a good proxy of readability. Here we have a change which makes general syntax highlighting of what is a distinct concept impossible, though the most common cases are possible, without semantic parsing of the structure of the language: it must know where lifetimes are permissible, which, among other things, includes making distinction between types and expressions in a way that hitherto has not been necessary (I will admit that that part of the argument alone is moderately weak). (Side thought, distinctly off-topic: by using librustc, actually running all the macros, and seeing what each token is parsed as, can one provide general syntax highlighting in macros?) I am surprised to find that |
As I've noted, this doesn't work very well with lifetime-parameterized types. I know everyone wants this but we internally debated this years ago and decided it didn't work. Furthermore, it preserves the unnatural-looking
I disagree. I think that anything with That suggestion is also less explicit, again because of lifetime-parameterized types. You could solve it by making the inferred region the intersection of any reference lifetimes and any type parameters, but that's adding a lot of complexity and magic and is also no longer explicit. You are only thinking of easy cases; consider something like
No, I wouldn't remove this restriction.
Yes.
I am totally fine with enforcing the case conventions, actually. I didn't bring it up because I felt it would be too controversial and getting away from C syntax. |
-1 to any hard capitalization rule. It would prevent us from ever allowing identifiers to be written in non-Western languages. |
I don't understand why the fact that this rule wouldn't apply to lifetime-parameterized types means the rule is not useful. The vast majority of lifetimes (at least, in my experience) would be able to use this rule. Sure, some lifetimes would still have to be explicitly declared, but nobody is saying they wouldn't. |
"And there's no reason to believe that random people you find who dislike ' will find case to be any better" Except a thread full of people saying exactly this? I have been consistently pushed away from Rust after seeing heaps of syntactic noise up front. A lot of the responses have been "theyre not heavy users, what do they know!" but, arent non-users the people you're trying to attract? If you make a language look complex, detailed and intimidating no one is going to use it. I am quite serious when I say this kind of noise is turning off a lot of people. The intimidation-factor behind a language is very important for its adoption, and syntax is a very big part of that. |
FWIW, just a general point (not specific to |
I agree with @huonw. Additionally, although many have expressed a dislike of the syntax, referring to examples that include the |
I'm always more inclined to weight decision making by level-of-interest/caring. If people do not care, what do they matter either way? There is no barrier to entry to the discussion (a free github account...). If you're concerned about representing the interests of the entire user base then the thing to do is publicize the issue to them, not have this discussion in a clairvoyant manner try to guess what the tastes of the people who dont care are. |
For me, the sigils is why I first liked Rust. It looked unique and fun. What puts me off in a language is things like lack of static typing or On Tue, Jul 1, 2014, at 11:07 AM, Michael John Burgess wrote:
|
It seems to me that the rust community was growing quite rapidly even back when we still had more sigils (~ and @) so they can't be all that off-putting. At the end of the day you can't please everyone, and lifetimes are always going to be something strange and difficult for newcomers. I think trying to hide them by removing |
For me the "weird" syntax for lifetimes (out of place for a language that took a lot from the C syntax) helped me understand that they were something different that needed deeper investigation. As others have pointed out, lifetime annotations are not found in python, C or Java, so in my opinion it's no use to pretend "nothing to see here, move along" to newcomers. Lifetimes are something you need to understand in order to use Rust effectively and blurring the distinction between types and lifetimes in declarations is not going to help that (the distinction between 'a and T is greater than between a and T IMHO, you know it's not just some weird coding convention, 'a and T are obviously different things, even if you don't know what yet). Even Python uses sigils for certain things like function decorators. Lisp and its famously simple syntax uses ' as a shorthand for the quote operator. So I think after reading this entire thread I'll side with the people saying that lifetimes annotations should be removed when they can be inferred. And when they can't it's no use pretending they're not weird. They are weird for 99% of the coders out there and that's part of what makes Rust interesting. Alternatively, if sigils are really an issue, I like the idea of having a "scope" keyword instead. Slightly more verbose but also much more explicit and (perhaps most importantly) google-able. Again, the verbosity wouldn't be much of an issue if the need for lifetime annotations is removed in the vast majority of cases. |
When did removing/replacing ' become the same as dooming newcomers to rust to confusion and complacency about lifetimes? I think most of the tutorials ive ever read about rust start with a discussion of ownership and lifetimes... I dont think an unclosed quote littered throughout source code has anything to do with whether a person will learn about a language feature or not. The danger here is Ugly == Important. Importance is very difficult to build into language design. Everything is important, must everything "stand out". IF so We''RE l.e.f.t. with A languageWhich MAKES EVERYTHING unread able. Readability and a flow aesthetic which makes readability paramount has been time-and-again the right decision for syntax choices. Learning the language cannot be substituted by using its syntax to clobber people over the head. |
@kballard's comment #134 (comment) Lifetimes are omitted today where they have very simple inference (No relations to other references or values at all). In spite of this, I prefer simplifying lifetime syntax instead of removing (inferring) in yet more cases: Making lifetime ties between input and output parameters inferred and thus invisible would be very confusing I think. |
Removing I also agree with @pcwalton's point that people would be universally against introducing Further, #141—while a great idea on its own—is completely orthogonal to this issue. Both RFCs should be evaluated independently (and IMO accepted). |
So I was thinking about parameterizing over mutability. Then I realized that @bstrie's proposal could make it really simple in terms of syntax. Keep in mind that the fn get<a&m>(a&m self) -> a&m T {
&m self.field
} And this syntax for lifetime parameters alone emerges from the above: fn foo<a&>(bar: &T, baz: a&T) -> a&T; |
@pczarn That's really quite unreadable. |
The fn get_mut<#a>(&#a mut self) -> &#a mut T; |
I also think the sigil is right especially since most lifetimes are automatically inferred by the compiler so they don't have to be written by the developer. Why not use a fn get_mut<$a>(&$a mut self) -> &$a mut T; |
Given the recent work to allow inferring of a lot more lifetimes, perhaps we should suspend any discussion on this RFC until we see how often people will still end up typing lifetimes. |
@kballard I know what you mean but I still see the problem of thousands of ugly syntax-highlighted StackOverflow questions. I don't think this is going away because the syntax is also used in error messages etc. Take a look at any StackOverflow question with Rust code that scratches lifetime issues. They are all formatted poorly because the syntax highlighting confuses Rust lifetimes with strings. http://stackoverflow.com/questions/17490716/lifetimes-in-rust |
Syntax highlighters are not something worse discussing here. Rust syntax has evolved and will continue to evolve, people will need to update their Rust code and so they will need to update syntax highlighters. |
The point is that |
@errordeveloper I think the syntax highlighting issue is pretty much the only thing that makes me support a change. Sure you can tell everyone to update their syntax highlighting but given that |
The syntax highlighter used on StackOverflow (which is apparently google-code-prettify) needs to be updated to handle lifetimes correctly. That's not an issue that requires fixing on Rust, it can be trivially fixed in the highlighter. Note that StackOverflow does not apply generic code highlighting but instead highlights code blocks for specific languages. The language is usually inferred from the tags on the post, but can also be specified explicitly with a particular syntax (using an HTML comment, I always forget exactly how). This means that there is no concern about some sort of generic highlighting getting it wrong for Rust, the only problem is that the Rust-specific highlighter is apparently not very good. |
#bikeshed |
Closing. Although improving the ergonomics of the language is an important topic and we're going to continue making improvements in this area, this specific proposal is not going to happen. |
If it makes any difference I gave up on rust finally after trying to write some ML stuff in it. The syntax was the issue. |
It's still possible to introduce this change while having backwards compatibility, by allowing |
[RFC] Add an unordered buffer stream adapter.
Summary
Remove the
'
from lifetime parameters. Instead, infer whether each generic parameter is a lifetime or a type.Motivation
As punctuation, the
'
notation is visually noisy, and it turns out to be unnecessary for both the compiler and the reader. The compiler can infer whether each generic parameter is a lifetime or a type parameter from its use, and a reader can infer the identity of a generic parameter from its case.'
as a generic parameter comes from the ML syntactic tradition, which is quite different from that of C. In particular, the overloading of'
to mean both characters and lifetimes can be jarring for anyone not used to OCaml or Perl 4. And mixing of ML-style'
generics and C++-style<>
generics is entirely without precedent.This has been presented (by some members of the Chromium team) as a particularly egregious example:
With the
'
dropped, this becomes significantly less noisy:Detailed design
We replace the
LIFETIME
token with theIDENT
token in all productions in the grammar. This will affect type parameters, reference types, and labels.When determining the semantics of a type declaration, the compiler looks for uses of each generic parameter within
<>
to ascertain its identity (type parameter or lifetime):&
, it is a lifetime.The labeled block syntax is unambiguous because we have solved the ambiguity between structure literals and labeled statements by syntactically restricting the positions in which structure literal expressions may be used per Rust PR #14885.
A lint pass will be added that warns if lifetimes do not begin with a lowercase letter or if type parameters do begin with a lowercase letter. This is important because code that does not maintain this style may mislead readers (though not the compiler).
Drawbacks
Alternatives
The impact of not doing this is that the drawbacks noted in "Motivation" above will persist.
Unresolved questions
None.