Writing / Detail

Rust for Frontend Devs: Variables, Mutability, and Shadowing

2026.03.31 (edited)
Rust
4333 Words
- Views
- Comments

Moving from JavaScript or TypeScript to Rust feels a bit like switching from a sports car with no seatbelts to a high-performance jet—there are more controls, but they're there to keep you from crashing. One of the first things you'll notice is how Rust treats data storage. Here’s a breakdown of how variables, constants, and shadowing work in the world of Rust.


1. Variables are Immutable by Default

In JavaScript or TypeScript, you are used to choosing between const (for values that don't change) and let (for values that do). In Rust, when you declare a variable using let, it is immutable by default. This means that once a value is bound to a name, you cannot change that value.

If you try to write code like this, where you assign a value to x and then try to change it later:

fn main() {
    let x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}

The Rust compiler will throw an error: cannot assign twice to immutable variable 'x'. Rust does this to guarantee safety and prevent bugs that happen when one part of your code expects a value to stay the same, but another part changes it unexpectedly.


2. Making Variables Mutable

When you actually want a variable to change (like a standard let in JS/TS), you have to explicitly tell Rust by adding the mut keyword in front of the variable name.

Here is how you fix the previous code:

fn main() {
    let mut x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}

Running this program will now successfully output 5 and then 6. Adding mut conveys your intent to future readers of the code that this variable's value will change.


3. Constants

Like JS/TS, Rust also has a const keyword. However, there are strict differences between an immutable let variable and a const in Rust:

  • You cannot use mut with constants; they are always immutable.
  • You must always explicitly annotate the type of the value.
  • Constants can only be set to a constant expression, not to a value that is computed at runtime.

Rust's naming convention for constants is to use all uppercase with underscores between words. Here is an example:

const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

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


4. Shadowing

Rust has a unique feature called shadowing that doesn't exist in JS/TS. Shadowing allows you to declare a brand-new variable with the exact same name as a previous variable. You do this by repeating the let keyword.

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}");
}

When you run this, it prints 12 for the inner scope, and then 6 for the outer scope.

Why use Shadowing instead of mut? Because shadowing effectively creates a brand-new variable using the let keyword, you can completely change the data type of the value while reusing the same, simple variable name.

For example, if you ask a user to input spaces and you want to store the number of spaces, you can do this:

let spaces = "   ";
let spaces = spaces.len();

The first spaces is a string type, and the second spaces is a number type. If you tried to do this using a single mut variable, Rust would throw a mismatched types compile-time error because you are not allowed to mutate a variable's underlying data type.