Reputation: 17170
I was interested in Rust, and so I started reading the Rust programming guide on the Rust website, and discovered that variables are declared with the following syntax:
let x: i32 = 5;
Which means assign the value integer 5 to the variable type integer 32 bit which shall be referred to by the notation x
from this point onwards.
Why is the let
keyword reequired? It seems redundant, what does it actually "do"?
I assume the compiler would be able to tell that the following is a variable (or const variable) declaration:
x: i32 = 5;
There doesn't appear to be a reason for a let
keyword, but presumably there is a smart reason because Rust is focused on safety. So what is that reason?
Edit: Addition: As function arguments, the let keyword is not required. Here is an example:
fn add1(x: i32) -> i32
{
x = x + 1
}
This seems a bit strange - this "looks like" a pass by reference because of the missing let
. But it's not. It's a pass by value. (Or at least I think it is.) Is this a syntactic inconsistency?
As an aside, why is the ordering of the variable name and type different from other languages? For example, the syntax could have been:
i32 x = 5;
Put a colon in there if you prefer.
i32: x = 5;
This brings me to another point; how can one declare a several variables of the same type in Rust? Such as:
let x, y, z: i32 = {4, 5, 5} // A guess of what this might look like?
Or is this just not allowed in Rust?
Upvotes: 18
Views: 9575
Reputation:
First, parsing ambiguity. While it may be possible to parse such code (but only because a < b > c
is already illegal as expression for a different reason), it would complicate all tooling and make parser performance (and error recovery) worse.
Second, type inference. Regardless of where the type is written, it can be omitted, so you need let
or another marker to distinguish assignment from declaration. (You could technically use let x = ...;
and Type x = ...;
but why? It's not a type, it's the absence of a type.)
Third, generality. let
works with any (irrefutable) pattern, including those that double as perfectly legal expressions: StructLiteral { field: x, other_field: y }
and Some(thing)
. This is a source of even more ambiguity, if not for the parser (it rejects Some(a) = b
currently), then for humans.
Fourth, while you may be used to type name
, there is a long tradition of name: type
, and it's gaining mainstream traction without Rust's doing (e.g. in Scala).
Fifth, I dispute this:
When programming you usually know what "type" of data you want before you think of a name for it.
Perhaps I know the general shape of the data (an integer, a string, an iterator, a set, etc.) but the exact type is often an implementation detail that comes second. This argument also falls apart in the face of type inference: How can leaving the type out be useful if that's what you are mostly thinking in? If on the other hand you're only talking about what you think of first while writing code, then the case is dismissed: Code is read far more often than it is written, and the order in which concepts are introduced need not (and often shouldn't) reflect the order in which they are conceived.
Upvotes: 13
Reputation: 15559
It significantly simplifies the grammar. Remember, there's a full pattern on the left hand side, it's not actually let var
it's let pattern
.
how can one declare a several variables of the same type in Rust?
This is a completely different question, but there's no direct way to do this, no, at least not in the way you're thinking of. You can
let (a, b, c) = (1, 2, 3);
But they don't have to be of the same type, this is a destructuring pattern in action.
Upvotes: 9
Reputation: 90832
Rust has local type inference, so the type normally doesn’t need to be written; let x = 5;
will suffice. x = 5;
would be quite different, as it would not declare a variable x
, and Rust very deliberately separates declaration and assignment.
It’s also actually let PATTERN = EXPR;
, not just let IDENT = EXPR;
, so removing the let
keyword would cause grammatical ambiguity. A pattern could be mut x
(making the variable binding mutable), it could be (a, b)
signifying tuple unpacking, &c.
You only think i32 x = 5;
makes sense because you’re used to languages like C++. Seriously, who came up with that idea? It makes more sense on purely philosophical grounds to have the type after the name than before, and having just the type to declare variables is silly too. All sorts of grammatical ambiguity there. Type inference, allowing you to omit the type altogether, is a much nicer approach all round.
Upvotes: 44