[rust-dev] First thoughts on Rust

Niko Matsakis niko at alum.mit.edu
Mon Jan 23 09:54:59 PST 2012


First off, Masklinn thanks for your feedback (I know others have said 
it, but I wanted to chime in).  This kind of thing is inordinately useful.

> *pcwalton: *Nobody could find a way to implement non-local returns in 
> a performant way, so they're missing. There's been discussion of a 
> magic "loopctl" enum type as a return type for blocks, which would 
> allow non-local returns with cooperation between the loop function and 
> the block.

I finally got around to writing up my idea 
<https://github.com/mozilla/rust/issues/1619>. It's not full-on 
smalltalk blocks but it would be straightforward to implement and nice 
to have.  Worse is better and all that.

>> *OP: ** The second issue is both trivial and extremely serious: after
>>    having written a few trivial pieces of code (can't call them
>>    programs), it feels like Rust's handling of semicolons combines
>>    the simplicity of Erlang's with the determinism of
>>    Javascript's.

I think the criticism is fair enough---even though I authored the 
current rules I am not sure they are best, though I prefer them to what 
we had before---but I do want to point out that the rules are not 
non-deterministic or anything like that.  In fact, I made a big effort 
to ensure that programs which are not parsed "as they appear" will 
result in compile errors.

For example, something like this:

     fn foo(v: [int]) -> int {
         vec::foldl(v, 0, {|x, y| x + y}) - 10
     }

looks like it sums up all of the integers in the list and subtracts 10, 
but in fact it parses as:

     fn foo(v: [int]) -> int {
         vec::foldl(v, 0, {|x, y| x + y}) // throw away the return value
         -10
     }

This would seem like a big problem, except that the type checker will 
report an error because vec::foldl() has a non-unit return type and an 
omitted semicolon.  Of course, if you're the innocent author who doesn't 
realize WHY you're getting an error, it can still be quite confusing!

Personally I think we were going to change the current rules I would 
want to just use a semicolon consistently after every statement, whether 
or not a similar statement would have a semicolon in C.  So you would write:

     fn foo() { ... }     // this is a declaration, no semicolon
     while test { ... }; // this is a statement, use a semicolon
     ret foo;

and so forth.  I would probably also make the trailing semicolon 
insignificant, as discussed below.  As pcwalton said, though, this would 
mean that we feel "even less" like C.

> *pcwalton: *I like OCaml's solution, which is to simply ignore 
> trailing semicolons. In order to do this we need to reform the 
> standard library to avoid returning values that are unused (which is a 
> good idea anyway, so this is a lesser problem). But IIRC when this was 
> tried, there were parsiing ambiguities involving loops and sugared 
> block syntax (basically, the set of expressions that automatically 
> infer a trailing semicolon). We would have to solve those issues somehow.

Actually, the parser was not the problem.  In fact, the parser does not 
currently look "inside" blocks and that sort of thing like it used to.  
The problem was that I tried to change the representation of a block 
from a pair of (stmts, tail expr) to just (stmts), and this triggered 
lots of changes.  If I were to try again, I think I would keep block the 
way it was.

What is needed, however, is a change to the type checker which says that 
tail expressions in a unit return context are effectively ignored.  In 
other words, I think that a function like:

     fn bar(...) -> int { /* some side effect-y thing */ }
     fn foo(...) {
         bar(...);
     }

should still type check, but if we get overeager on the unification it 
will not. I also made this change to the type checker.  It was minor.  
It seemed to work fine.  I'm sure it might give a somewhat surprising 
error in some corner cases, like blocks where we are inferring the 
return type (those need work anyhow), but that's probably ok.

In the end, though, I decided to go the more conservative route and not 
modify the type checker but instead simply require that statements 
without a trailing semicolon must have unit type.

All that said, I think what the OP was complaining about was not knowing 
when a semicolon is required and when it is not.  This can be a bit 
confusing.  The rule is more-or-less "if it ends with braces, it does 
not need a semicolon", but this is not true when e.g. the expression is 
embedded in a `let` (as, then, the `let` itself needs a semicolon).

So the following are fine and do not require semicolons:

     foo() {|| ... }
     if ... { ... } else { ... }

but this does:

     let x = foo() {|| ... };

>> *OP: ** Finally, I could not find any good information on the result 
>> of loop
>>    expressions, from my tests it seems to always be `()` is that
>>    correct? If so, why `()` rather than the last-evaluated result of
>>    the iteration? In case no iteration at all is performed?

By the by, I believe that do-while loops return the value of the final 
iteration (they always have at least 1).


Niko


More information about the Rust-dev mailing list