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
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to