Variables are immutable by default.

let apples = 5; // immutable
let mut bananas = 5; // mutable

Shadowing

Rust allows us to shadow the previous value of a given variable with a new one, e.g.:

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}");
}
// Output:
// The value of x in the inner scope is: 12
// The value of x is: 6

This feature is often used when you want to convert a value from one type to another type.

Constants

const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

The type of the value must be annotated.

May be set only to a constant expression, not the result of a value that could only be computed at runtime.

Constants are valid for the entire time a program runs, within the scope in which they were declared.

Scope

Every variable has its scope where itโ€™s valid.

Clone

We can use a common method called clone:

let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);

Rust has a special annotation called the Copy trait that we can place on types that are stored on the stack, as integers are (weโ€™ll talk more about traits in Chapter 10). If a type implements the Copy trait, variables that use it do not move, but rather are trivially copied, making them still valid after assignment to another variable.

Rust wonโ€™t let us annotate a type with Copy if the type, or any of its parts, has implemented the Drop trait. If the type needs something special to happen when the value goes out of scope and we add the Copy annotation to that type, weโ€™ll get a compile-time error.

Rust doesnโ€™t have the null feature that many other languages have. Null is a value that means there is no value there. In languages with null, variables can always be in one of two states: null or not-null. Instead we have Option enum.