"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.
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).
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.
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...
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 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.
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);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.
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. *( arraybase + f1( d ) * f2( e, f ) + f3( g ) ) = x;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.
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.
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.