News for hackers. Stuff that matters.
Re:Rust for C++ programmers – part 2: control flow by acqq on Sunday April 20, 2014 @03:53AM (#7616178)
I still find it's a pity that so new language has "you must write let" that even Basic programmers don't have to do during at least 30-40 years:

https://www.powerbasic.com/support/help/pbcc/let_statement.h...

I remember not having to write let in Basic even in eighties. Anyway, since 1995, http://en.wikipedia.org/wiki/Limbo_(programming_language) people have an idea that there is a simple way to have "define and assign" that is different from "assign" without requiring a keyword: use := for define and assign, x:=exp is equivalent to let x=exp, use = for just assign. Google Go just reused that idea. Even if it's used today by Go, I still don't understand why Rust avoids it, because it makes sources significantly more readable: the variable is not obstructed by the keyword.

let mut is even more "stuff" before the variable in the language where declarations otherwise always follow the variable (which is good!) I'd use x := expression and x : mut = expression.

Re:Rust for C++ programmers – part 2: control flow by pcwalton on Sunday April 20, 2014 @03:56AM (#7616186)
It wouldn't work in Rust because "let" supports arbitrary (irrefutable) patterns, and Rust has full pattern matching. The parser has to know when a pattern is coming up. Languages that use :=, like Go, don't have a complete set of destructuring features. The convenience of being able to have an arbitrary pattern in "let" bindings far outweighs the disadvantage of having to type "let".

"x : mut = expression" wouldn't work in Rust because types follow :, and "mut" is not part of a type. "mut" is needed after each binding because one pattern can contain some mutable bindings and some immutable bindings.

Re:Rust for C++ programmers – part 2: control flow by jnbiche on Sunday April 20, 2014 @07:58AM (#7616542)
As someone who is learning OCaml at the same time as Rust (and who already had some Haskell), I can attest that the power of pattern matching is well-worth the extra "let". Plus, in addition to the signal it provides to the compiler, it also provides me a signal to know that I've got a new variable and the option here to do destructuring pattern matching. Very cool.

pcwalton: after getting over the hump of Rust's pointer semantics, I've found that it's a very elegant and fairly simple language. So I was partially wrong about my assessment of Rust as a "complex" language (I say "partially" because the pointer semantics do add a significant layer of unavoidable complexity).

I'd encourage anyone put off by the complexity of Rust's pointers to just push on through. I think you'll find it's worth the trouble.

Rust is definitely the language of the future for games, embedded programming, system programming and other areas of programming that require a high level of determinism. I can't wait until it's stable enough to use for real projects (hopefully sometime this year).

Re:Rust for C++ programmers – part 2: control flow by bjz_ on Sunday April 20, 2014 @08:56AM (#7616638)
> after getting over the hump of Rust's pointer semantics, I've found that it's a very elegant and fairly simple language

Definitely, I cannot stress this enough. Back when I first looked at the language in 0.3 it was awfully complex and I had doubts about hwo successful it would be. But ever since 0.4, with the removal of classes and argument modes, the core devs have been working extremely hard to make the core semantics of Rust as simple as possible, whilst at the same time making the language powerful and extensible. It will take time to dispel the myths still hanging around though, so I would urge you to share your experiences more widely.

Re:Rust for C++ programmers – part 2: control flow by steveklabnik on Sunday April 20, 2014 @12:48PM (#7617456)
Re:Rust for C++ programmers – part 2: control flow by jnbiche on Sunday April 20, 2014 @08:12PM (#7618991)
Yes. That was the guide, along with the pointer semantics table, that basically convinced me to give Rust a second chance, since if pointers could be summed up in a table, they were definitely comprehensible even for a PL peon like me.
Re:Rust for C++ programmers – part 2: control flow by steveklabnik on Sunday April 20, 2014 @08:35PM (#7619063)
Cool, thank you. I was wondering if it helped or hurt. Great!
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.
Re:Rust for C++ programmers – part 2: control flow by mpartel on Sunday April 20, 2014 @04:15AM (#7616224)
Having programmed a lot in both Scala and Ruby, I'd argue the opposite. The keyword makes new variables easier to spot quickly, and with syntax highlighting, I really don't see `let [mut]` as clutter. Admittedly tastes in such things will always vary.
Re:Rust for C++ programmers – part 2: control flow by sixbrx on Sunday April 20, 2014 @02:17PM (#7617846)
I actually like "let" to introduce variables, it's what I scratch on paper when doing psuedo-code since before I met Rust or any other functional language. To omit it (or something like it, e.g. "var" or "val") would be to omit crucial information. It's short, clear, and has a long history in mathematics and logic too.
Re:Rust for C++ programmers – part 2: control flow by Theriac25 on Sunday April 20, 2014 @06:00AM (#7616374)
I'd like to inform you that you have bad taste and your post doesn't have anything to do with the article.