Rust already amazes a lot when it comes to the basic language features. Let’s, for example, take a look at shadowing and temporary mutability. Both, fairly basic and simple language features, yet powerful and a real booster for robustness and correctness.

Shadowing in Rust

Shadowing is such a simple language feature. However, it has a significant effect on the code you write daily. Shadowing allows you to re-declare a variable in the same scope, using the same name. The re-declared variable differs from the original by having a different type. This is especially useful upon casting data from one type into another. In Rust, you can achieve this without adding unnecessary application-state.Take the following code as an example:

    let mut number = String::new();

    io::stdin().read_line(&mut number)?;
    let number = number.trim().parse::<i32>()?;

The Rust compiler (rustc) recognizes the shadowing operation and guarantees that ongoing code is (in this example) dealing with an i32 instead of String. Although, same the same logic requires less typing in other languages (such as C#), I like the fact that I don’t have to add application state.

// C#
var number = Console.ReadLine();
var numberAsInt = Int32.Parse(number.Trim());

Shadowing and Scopes in Rust

Shadowing also plays nicely with nested scopes in Rust. It makes the code even more readable and adds the possibility of “falling back” to the initial type/value of the shadowed variable. Take a look at the following example. The firstname variable will be shadowed in a dedicated scope. After leaving the scope, the associated stack will is destroyed, and the original firstname variable is used again.

fn main() {
    let lastname = "Smith";

    let firstname = "John";
    println!("My name is {}, {} {}", lastname, firstname, lastname);
    {
        println!("> My name is {}, {} {}", lastname, firstname, lastname);
        let firstname = "Mike";
        println!("> My name is {}, {} {}", lastname, firstname, lastname);
    }
    println!("My name is {}, {} {}", lastname, firstname, lastname);

    /* output:
    My name is Smith, John Smith
    > My name is Smith, John Smith
    > My name is Smith, Mike Smith
    My name is Smith, John Smith
     */
}

Thanks to minigamedev for suggesting this one.

Temporary mutability in Rust

Variables in Rust are immutable by default. That said, you can’t mutate the value of a variable once it’s initialized. Your application will not compile.

fn immutable_name() {
    let name = "John";
    println!("Hello, {}", name);
    // prints Hello, John

    name = "Mike";
    // will not compile, because name is immutable
}

fn immutable_name_long_version() {
    let name: &str;
    name = "John";
    println!("Hello, {}", name);
    // prints Hello, John

    name = "Mike";
    // will not compile, because name is immutable
}

However, you can make any variable mutable by adding the mut keyword upon variable declaration:

fn mutable_age() -> u16 {
    let mut age = 35;
    // works because age is mutable
    age = 36;
    age
}

The variable age in the previous example is immutable for the entire scope. By re-declaring the age variable as immutable, the Rust compiler can prevent us from accidentally doing state mutation if not intended:

fn temp_mutable_age() -> u16 {
    let mut age = 30; 
    // age is mutable
    age = 40; 
    let age = age;
    // age is immutable
    // age = 50 // 👈🏻 would not compile, because age is immutable at this point!
    age
}

Conclusion

So far, learning Rust is fun. It’s refreshing, also challenging when it comes to more complex language characteristics. Finding small nuggets like Shadowing and Temporary Mutability increases the fun and makes me look more and more into the language, its standard library, and the ecosystem.