10 ms·
Such a Little Thing: The Semicolon in Rust
- kibwen 14y agoI memorably referred to Rust's significant semicolons as "the worst thing in the language" after my very first read through the tutorial, last November. Almost a year later, I'm just as in love with Rust's semicolon rules as Armin is. However, I bet new users will still be just as instinctively revolted as I initially was.
- eridius 14y agoI guess it depends on what languages you're experienced with. I just read through the Rust tutorial for the first time 2 days ago, and I fell in love with the semicolon rule at first sight.
- deleted 14y ago[deleted]
- comex 14y agoNeat. I came up with this rule some time ago for my as-yet-unimplemented pet language, but I didn't realize Rust did the same thing.
- qznc 14y agoIs that really some "special behavior"? As far as i understand the semicolon is just an expression separator like in Erlang and Pascal. In contrast, the semicolon is a statement terminator in C. C uses the comma for expression separation. Practically everything is an expression in Rust, even blocks, so foo() => evaluates to the return value of foo { foo() } => evaluates to the return value of foo { foo(); } => evaluates to nil The last case is not a special case. The last expression in the block determines the returned value. There is an expression separator in there, so there must be two expressions which are separated by it. The first one is foo() and second one is ... wait for it ... the empty expression. Which value should EmptyExpression have? Of course: nil, which would be called void in C-land.
- WalterBright 14y agoThere was some discussion about doing this in D, and there was some in C++11, too. I argued that the presence or absence of the ; did not visually stand out very well, and so would be a source of confusion and errors. Hence, ; remained as a statement terminator, and the return keyword served to indicate returning an expression. However, the D lambda syntax does not require a ; when the lambda body consists of only a single expression: http://dlang.org/expression.html#Lambda http://dlang.org/expression.html#Lambda and in practice this has turned out to be well liked.
- pcwalton 14y agoIgnoring a potentially wanted return value with ";" is a hazard. I'd like to institute some set of "warn-unused-result" warnings in Rust to combat this.
- pdw 14y agoWouldn't the type system complain that you're returning nil in a function that is not declared/inferred as returning nil?
- MartinCron 14y agoAre semicolons annoying to type? Probably, I got used to them The semicolon is the least-annoying non-letter character to type. It's right there on your home row.
- glennsl 14y agoHello American. I'm from one of those other pesky countries that make up most of the human population of earth, and which usually have other keyboard layouts. I have to type shift+, to get the semicolon.
- bla2 14y agoI switch from my native layout to the US layout for coding.
- jlmendezbonini 14y agoWhich keyboard layout is that?
- dbaupp 14y agoSwedish is one such keyboard.
- DanBC 14y agoCzech; German; Polish; French; Spanish; Italian; Portuguese; etc etc etc.
- adamnemecek 14y agoThat being said, there are also programmer's layouts specific to single languages which among others have a semi-colon on the home row.
- naitbit 14y agoI've never seen QWERTZ keyboard in Poland and I live there. Typewriters indeed were using this layout, but not many people use them to program. "practically all computers (except custom-made, e.g., in public sector and some Apple computers) use standard US layout (commonly called Polish programmers layout, in Polish: polski programisty) with Polish letters accessed through AltGr (AltGr-Z giving “Ż” and AltGr-X giving “Ź”)." http://keyboard-layout.info/#Polish http://keyboard-layout.info/#Polish
- lihaoyi 14y agoThe downside is that you would have to put () (Rust's version of “nil”) in a bunch of functions to fulfil the requirements of the callback's signature since otherwise the type inferred from the function would be the value of the last expression Wouldn't co/contra-variance solve this entirely? It works just fine in Scala for example scala> def runTwice(f: () => Unit) = { | f() | f() | } runTwice: (f: () => Unit)Unit scala> runTwice{ () => | println("moo") | 1 | } moo moo Note how it expects a function that returns Unit, i'm passing in a function that returns an Int (1), but the compiler is perfectly happy.
- pcwalton 14y agoI implemented basically this in Rust, and it was overwhelmingly rejected by the community at the time. People seem to like the strict typechecking.
- drivebyacct2 14y agoSome of this seems cool and some of it is soaring over my head. I'm kind of embarassed and newly motivated to learn Rust.
- tome 14y agoTake a look at Haskell for a very clean and expressive syntax without (required) semicolons.
- Dobbs 14y agoI'd rather have significant semi-colons than significant whitespace[1]. Don't mix presentation and semantics. 1: http://c2.com/cgi/wiki?SyntacticallySignificantWhitespaceConsideredHarmful http://c2.com/cgi/wiki?SyntacticallySignificantWhitespaceCon...
- lucian1900 14y agoJust like Python, Haskell only ever has significant indentation.
- eridius 14y agoUntrue. While it's true that most people use the 'layout' rule, which is the significant whitespace, there is a non-whitespace-significant variant that uses braces and semicolons. You can see an example of how layout is expanded to the alternative syntax at http://www.haskell.org/onlinereport/lexemes.html#lexemes-layout http://www.haskell.org/onlinereport/lexemes.html#lexemes-lay...
- lucian1900 14y agoNo, I meant that Haskell doesn't consider any whitespace that isn't indentation as significant.
- eridius 14y agoVery few languages consider non-indentation whitespace as significant (not counting using whitespace as a token delimiter, which Haskell definitely does, e.g. "foo bar" =/= "foobar").
- 14y ago
- deleted 14y ago[deleted]
- deleted 14y ago[deleted]
- plaguuuuuu 14y agoIs it just me or is this kind of ugly in the first place? We're trying to mix logic that executes on each element in a sequence, with logic that controls how to iterate over that sequence. That is, a "return 42" statement will tell .each to stop iterating, but its contained within a block that is supposed to do things to individual elements. The mathematical concept just doesn't sit right with me.. I guess if all you have as an iterator is 'each' then that would necessitate finding an additional way to modify iteration in some way, but still.. I don't like the fact that a return statement can break out of something outside of its own scope edit: even the low-level alternative seems nicer for(blah;blah;blah) { stuff; }
- msluyter 14y agoI'd just like to express my appreciation for the depth of this article. I always learn something from Armin Ronacher.
- windle 14y agoI still don't understand why they couldn't just introduce a new keyword that has the same meaning as a missing semicolon. Call it 'ret' or something as a 'lesser return'. Relying on ppl to be semicolon hunters is poor for readability. It also means you can't put more expressions on the same line because doing so requires a semi-colon which then eliminates the special semicolon behavior. Having an explicit 'ret' keyword means you could accomplish that and have more expressions for a separate block on the same line if desired.
- daakus 14y agoI haven't looked at Rust, but the semi colon looks like a decision to make some common verbose or "ugly" syntax to be less noisy. Admittedly most syntax has quirks, and this seems more like a quirk rather than an instance of "clever design". Ignoring the explanation of the differences between statements and expressions, the rest of the discussion is about the presence of a semicolon.
- yxhuvud 14y agoI'm all with the author with the Ruby love and it looks like Rust managed to implement them in a cleaner syntactic way compared to Ruby. However, that template code was utterly hideous.
- kibwen 14y agoNormally in Rust you'd never use a trait like that without bringing it into the local namespace, so you wouldn't need to qualify it with its module like Armin does. And in future versions of Rust, Num will inherit from both the Eq and Copy traits, so the signature would instead look like this: fn find_even<T: Num>(vec: &[T]) -> Option<T> {
- lucian1900 14y agoAnd with Rust's strong static typing, it's even easier to spot. While I love a preference for expressions, in CoffeeScript I've had to put a single null at the end of functions several times. The presence/absence of the ; is both explicit and subtle enough to not annoy.
- enjolras 14y agoActually, that's a lot of words to say that ; is a binary operator, if i'm correct. a ; b is the operator which returns the value of b. And, there is some syntactic sugar to make a ; equivalent to a ; nil which returns nil.
- Scramblejams 14y agoMy kingdom for a TL;DR! (As a prerequisite to making its point, the article teaches many intricate details regarding two dynamic languages which even most of their practitioners probably never think about, then dives into more intricate details regarding a language which most of us have probably never even used, let along grokked all the details of. I sense that there's something important here for me to learn, but from where I sit this article is a lot to bite off all at once.) A tight summary by someone who understands all this would be appreciated by many readers, not just myself.
- irahul 14y ago> A tight summary by someone who understands all this would be appreciated by many readers, not just myself. A tight summary of all of it would be as long as the article. However, the point about semicolons in Rust is: 1. ; is a separator, not a terminator. 2. a;b separates a and b. 3. a; is a special case which means a;nil(or whatever is the equivalent in Rust) 4. The last expression in a function will be the return value of the function. 5. If the last line in a function is "a", it returns a. If it's "a;", it returns nil(from 3)
- Scramblejams 14y agoPerfect, thank you.
- cmccabe 14y agoI don't think parent deserves to be modded down. The article did take a pretty long time to get to the point. Why all this talk about Python and Ruby? Rust is not really competing with those languages-- it is quite specifically designed as a C++ replacement. They should be comparing themselves against C++, Golang, or D. The Golang solution would just be a function which returns the next element or nil if there are no more elements. That function might be part of an interface, if you wanted to generalize it across several types. To me, this is a lot simpler than the other stuff that was discussed. To go back to Rust specifically, rather than having magic semicolons, why not make the "break" statement take a value, which becomes the return value of the block? Despite all the tl;dr there was not much discussion of design alternatives.
- dllthomas 14y ago"But the alternative to semicolons is making line endings significant." Only sorta - consider Haskell's indent rule: code which is part of an expression should be indented further than the start of that expression. Generally, this is something you should be doing to make your code readable regardless of the statement termination.
- peripetylabs 14y agoAs an aside, the "power_it" function in Python can also be written as: map(lambda t: t ** 2, [1,2,3,4]) or even: [(lambda t: t**2)(x) for x in [1,2,3,4]]
- irahul 14y ago> [(lambda t: t2)(x) for x in [1,2,3,4]] Why would you have that lambda? [x**2 for x in range(1, 5)]