News for hackers. Stuff that matters.
Re:Rust for C++ programmers – part 2: control flow by acqq on Sunday April 20, 2014 @04:12AM (#7616219)
Can you please point me to the examples of the kind of the pattern matching constructs that don't allow using := ? I know it's obvious to you, I don't have your experience but I'd really want to understand. Thanks in advance.

Regarding "mut is not a part of a type," so what? From my perspective (of somebody who implemented just much simpler languages than Rust) it's something like a "storage attribute." It doesn't prevent me to build the syntax to be more readable: it's a keyword anyway. It's certainly not that it can't be there because otherwise it's unparsable.

Having x = expr, x := expr and x : mut = expr make all pretty clean and obvious to me...

Re:Rust for C++ programmers – part 2: control flow by bjz_ on Sunday April 20, 2014 @08:29AM (#7616595)
I would check out: http://pzol.github.io/getting_rusty/posts/20140417_destructu...

For example:

    struct Foo { x: (uint, uint), y: uint }

    let foo = Foo { x: (1, 2), y: 3 };
    let Foo { x: tuple @ (a, b), .. } = foo; // => a == 1; b == 2; tuple == (1, 2)
In terms of mut:

    let (mut x, ref y, &z) = (1, 2, &3);
    x = 2;   // reassignment is ok, because we bound `x` as mutable
    x += *y; // we bound `y` as a reference, so it must be derefed first
    x += z;  // we used a `&` pattern to get the value stored in the reference, so
             // derefing is not necessary
    *y += 1; // this would not compile, because y is immutable
Re:Rust for C++ programmers – part 2: control flow by acqq on Sunday April 20, 2014 @09:02AM (#7616658)
Thanks. Now I see that my preferred approach would collide with current declaration logic of having mut ref and & in front of the names. Otherwise it would seem possible

    foo := Foo { x: (1, 2), y: 3 };
    Foo { x: tuple @ (a, b), .. } := foo; 
    ( x: mut, y: ref, &z ) := (1, 2, &3);
    xx: mut := 33;
I see that having "x: mut" has one character more than "mut x" but I don't believe it makes the text less readable, quite the contrary because it's certainly less surprising when everything that describes the variable follows the ":"

Thanks a lot, I've certainly learned more about Rust this way.

Re:Rust for C++ programmers – part 2: control flow by bjz_ on Sunday April 20, 2014 @09:18AM (#7616689)
Showing let bindings with their explicit type declarations might help:

    let mut x: int = 2;
    let x: &mut int = &mut 2;
    let (mut x, ref y, &z): (int, int, &int) = (1, 2, &3);
The pattern binding goes to the left of the `:`, and the input type goes to the right. Also note that you can pattern match on types too:

    let (mut x, ref y, &z): (_, int, &_) = (1, 2, &3);
Re:Rust for C++ programmers – part 2: control flow by acqq on Sunday April 20, 2014 @10:08AM (#7616802)
Thanks, if _ is allowed as the type, why are explicit types written at all?

Is the last example equivalent to

    let (mut x, ref y, &z): (_, _, &_) = (1, 2, &3);
and can we go even further

    let (mut x, ref y, &z) = (1, 2, &3);
If we can, when do we actually need specifying the types? To do the type conversion or what?

Probably because I don't know Haskell (sorry) but I still imagine x y and z as names and even mut, ref and & as the "access" specifications, which define somehow the "type" of x y and z and not the type the thing accessed with the given name.

Re:Rust for C++ programmers – part 2: control flow by bjz_ on Sunday April 20, 2014 @11:18AM (#7617025)
I might not have been clear enough. I was just trying to show how the current syntax fits in with `:`, and why your suggestions for `: mut` would not fit with that. All of these are valid Rust code:

    let (mut x, ref y, &z): (int, int, &int) = (1, 2, &3);
    let (mut x, ref y, &z): (_, _, &_) = (1, 2, &3);
    let (mut x, ref y, &z) = (1, 2, &3);
The types are still the same in each case. No conversions or dynamic typing is happening in any of them - they are all statically typed.

Rust, like Haskell and ML, uses an advanced form of type reconstruction called Hindley-Milner[0] type inference. This is a constraint solving algorithm that can deduce types from very little information. Sometimes you do need to add some annotations though to help the inference along a little, but that is very rare (although more common in Rust than in Haskell or ML).

The grammar could be expressed as:

    'let' <pattern> (':' <type>)? '=' <expr>
Where `<type>` is the type of the expression to the right of the `=`. As I said before: in practice it is very rare to explicitly annotate a `let` binding.

[0]: http://en.wikipedia.org/wiki/Hindley-Milner

Re:Rust for C++ programmers – part 2: control flow by gnuvince on Sunday April 20, 2014 @09:36AM (#7616738)
Pretty sure your parser would need unbounded lookahead to support this syntax.
Re:Rust for C++ programmers – part 2: control flow by acqq on Sunday April 20, 2014 @01:22PM (#7617596)
What do you think how the parser parses anything on the left side, e.g. in C:

    *( arraybase + f1( d ) * f2( e, f ) + f3( g ) ) = x;
Re:Rust for C++ programmers – part 2: control flow by pcwalton on Sunday April 20, 2014 @05:52PM (#7618618)
The issue is that "expression" is the same nonterminal in C, whereas "expression" and "pattern" are separate nonterminals.
Re:Rust for C++ programmers – part 2: control flow by acqq on Monday April 21, 2014 @03:35AM (#7620178)
In C a difference still exists between a lvalue and a rvalue. Even if you can unify something at one level, you have to generate completely different code. C++ is even harder. So I can imagine something can be done when there would be an intention, probably something would have to be a bit more unified. But I guess nobody even tried.

BTW, I really like the basic goals of Rust, namely making something directly linkable with C but having as much as possible safety and more modern features. And I appreciate the work of the developers of Rust. Even introductory materials would benefit by promoting what we can do that wasn't as safe to be done in C.

Re:Rust for C++ programmers – part 2: control flow by acqq on Sunday April 20, 2014 @08:26AM (#7616588)
I've tried to find any meaningful example of "why not." Is "let" supposed to be needed because of something like this?

   let (a, b) = get_tuple_of_two_ints();
I don't see that it's fundamentally different to parse if it would be written like this:

   (a, b) := get_tuple_of_two_ints();
It's true that the parser has to wait for := to do know the semantics of the expression on the left side of it, but it can definitely be done.
Re:Rust for C++ programmers – part 2: control flow by pcwalton on Sunday April 20, 2014 @04:38PM (#7618397)
It would require making "expression" and "pattern" into the same nonterminal in the grammar, and then deciding afterwards whether something that was parsed was an expression or a pattern. This is sometimes called a "cover grammar" because it "covers" two distinct nonterminals. It is possible--ECMAScript 6 does it--but it's definitely a complex way to parse. It doesn't seem compatible with Rust's design goal of being easy to parse and regular, e.g. for tooling.
Re:Rust for C++ programmers – part 2: control flow by bjz_ on Sunday April 20, 2014 @09:08AM (#7616668)
Rust can pattern match on structs, fixed length vectors, references, and it has keywords to bind them as mutable and/or by-ref. Doing all that without a `let` would extremely challenging.