In Rust, shadowing is a feature that allows you to redeclare a variable with the same name. It differs from mutability by creating a new variable binding that overrides the previous variable of the same name. This helps reuse variable names within a scope while changing their type or value, improving code readability and flexibility.

Basic Concepts of Shadowing

Shadowing is achieved by redeclaring a variable using the let keyword. For example, you can first declare an integer variable and then shadow it as a string:

fn main() {
    let x = 5;
    println!("x = {}", x); // Output: x = 5
    let x = "hello";
    println!("x = {}", x); // Output: x = hello
}

Note: Shadowing does not change the memory location of the original variable; instead, it creates a new binding, and the original variable becomes inaccessible after shadowing.

Difference Between Shadowing and Mutability

Shadowing is often confused with mutability (using mut), but they are fundamentally different. Mutability allows modifying the value of a variable, while shadowing allows changing the type of a variable or reassigning it. Compare the following examples:

fn main() {
    // Using mutability
    let mut y = 10;
    y = 20; // Modify value, type unchanged
    println!("y = {}", y); // Output: y = 20
    // Using shadowing
    let z = 30;
    let z = "world"; // Change type
    println!("z = {}", z); // Output: z = world
}

The advantage of shadowing is that it can adapt to different data types without introducing new variable names, which is very useful when dealing with complex logic.

Scope Rules of Shadowing

Shadowing is only effective within the current scope. When leaving the scope, the original variable becomes available again. For example:

fn main() {
    let a = 1;
    {
        let a = 2; // Shadow a
        println!("inner a = {}", a); // Output: inner a = 2
    }
    println!("outer a = {}", a); // Output: outer a = 1
}

Warning: Excessive use of shadowing may make code difficult to debug. It is recommended to use it only when there is a clear need to change types or reuse variable names.

Practical Examples of Shadowing

Shadowing is particularly useful when parsing user input or converting data types. The following example shows how to convert string input to an integer:

fn main() {
    let input = "42";
    let input: i32 = input.parse().expect("Not a number!"); // Shadow as integer
    println!("Parsed input: {}", input); // Output: Parsed input: 42
}

This avoids creating multiple temporary variables, making the code more concise.