Constants are valid for the entire time a program runs, within the scope in which they were declared. This property makes constants useful for values in your application domain that multiple parts of the program might need to know about, such as the maximum number of points any player of a game is allowed to earn, or the speed of light
Shadowing is where you reuse an existing variable name. You can shadow a non-mutable variable, which creates a new variable and assigns it to the existing name.
This also means that you can change the type of a variable when shadowing it, since it’s a different variable. If you tried changing the type of a mutable variable it would fail.
You can create new scopes within others by wrapping blocks of code within curly brackets. Anything within this newer scope doesn’t impact outer scope, including any shadowing of variables which are defined in the outer scope:
fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("The value of x in the inner scope is: {x}");
}
println!("The value of x is: {x}");
}
Note that number literals that can be multiple numeric types allow a type suffix,
such as 57u8, to designate the type
Tuples have a fixed length: Once declared, they cannot grow or shrink in size.
let tup: (i32, f64, u8) = (500, 6.4, 1);
let (x, y, z) = tup;
let firstVal = tup.0;
The tuple without any values has a special name, unit. This value and its
corresponding type are both written () and represent an empty value or an
empty return type. Expressions implicitly return the unit value if they don’t
return any other value.
Unlike a tuple, every element of an array must have the same type. Unlike arrays in some other languages, arrays in Rust have a fixed length.
let a = [1, 2, 3, 4, 5];
let b: [i32; 5] = [1, 2, 3, 4, 5]; // Here, i32 is the type of each element. After the semicolon, the number 5indicates the array contains five elements
let c = [3; 5]; // The array named c will contain 5 elements that will all be set to the value 3 initially
let first = a[0];
let second = a[1];
A vector is a similar collection type provided by the standard library that is allowed to grow or shrink in size because its contents live on the heap. If you’re unsure whether to use an array or a vector, chances are you should use a vector.
In function signatures, you must declare the type of each parameter. This is a deliberate decision in Rust’s design: Requiring type annotations in function definitions means the compiler almost never needs you to use them elsewhere in the code to figure out what type you mean. The compiler is also able to give more-helpful error messages if it knows what types the function expects.
let y = {
let x = 3;
x + 1
}