
Such a Little Thing: The Semicolon in Rust - joeyespo
http://lucumr.pocoo.org/2012/10/18/such-a-little-thing/
======
qznc
Is 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
There 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>

and in practice this has turned out to be well liked.

~~~
qznc
An understandable point of view.

Though I can think of an even more crazy variation. Declare the semicolon a
binary operator and allow overloading it.

As funny as that sounds, Haskell provides something like this, as do-notation
can behave differently depending on the monad it is in.

~~~
ajuc
Your post made me check it, and you CAN overload "operator," in C++ apparently
:)

[http://en.wikibooks.org/wiki/C%2B%2B_Programming/Operators/O...](http://en.wikibooks.org/wiki/C%2B%2B_Programming/Operators/Operator_Overloading#Comma_operator)

My mind is blown. You can write bottom-up code in C++ :)

EDIT: no, you still can't the expressions are evaluated first, before calling
"operator,"

------
kibwen
I 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
I 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.

------
lihaoyi

        The 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
I implemented basically this in Rust, and it was overwhelmingly rejected by
the community at the time. People seem to like the strict typechecking.

------
MartinCron
_Are 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
Hello 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.

~~~
jlmendezbonini
Which keyboard layout is that?

~~~
DanBC
Czech; German; Polish; French; Spanish; Italian; Portuguese; etc etc etc.

~~~
adamnemecek
That being said, there are also programmer's layouts specific to single
languages which among others have a semi-colon on the home row.

------
plaguuuuuu
Is 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
I'd just like to express my appreciation for the depth of this article. I
always learn something from Armin Ronacher.

------
windle
I 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.

------
tome
Take a look at Haskell for a very clean and expressive syntax without
(required) semicolons.

~~~
Dobbs
I'd rather have significant semi-colons than significant whitespace[1]. Don't
mix presentation and semantics.

1:
[http://c2.com/cgi/wiki?SyntacticallySignificantWhitespaceCon...](http://c2.com/cgi/wiki?SyntacticallySignificantWhitespaceConsideredHarmful)

~~~
orlandu63
I'd rather have the ability to choose between and freely mix significant
whitespace and significant semi-colons within a single source file.

~~~
lmm
No you wouldn't. See Javascript for all the pain that causes. Language syntax
needs to be dictatorial so that the language is consistent for reading (which
is more important than writing).

~~~
joeyespo
JavaScript's problem isn't that you can choose whether or not to use
semicolons, it's that they're automatically put in for you if you omit them.

[http://lucumr.pocoo.org/2011/2/6/automatic-semicolon-
inserti...](http://lucumr.pocoo.org/2011/2/6/automatic-semicolon-insertion/)

Technically, Python is a much better example of optional semicolons since you
can use them and they're not required. Of course, the real reason they exist
is for compound statements.

[http://stackoverflow.com/questions/8236380/why-is-
semicolon-...](http://stackoverflow.com/questions/8236380/why-is-semicolon-
allowed-in-this-python-snippet)

------
enjolras
Actually, 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.

------
yxhuvud
I'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
Normally 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> {

------
daakus
I 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.

------
dllthomas
"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.

------
lucian1900
And 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.

------
peripetylabs
As 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
> [(lambda t: t __2)(x) for x in [1,2,3,4]]

Why would you have that lambda?

    
    
        [x**2 for x in range(1, 5)]

------
Scramblejams
My 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
> 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
Perfect, thank you.

------
comex
Neat. 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.

------
drivebyacct2
Some of this seems cool and some of it is soaring over my head. I'm kind of
embarassed and newly motivated to learn Rust.

