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.
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...