In Java, variables are mutable unless you slap final on them. Most developers don’t bother, so everything ends up mutable by default — even when it doesn’t need to be.

Rust flips this. Variables are immutable by default. You have to explicitly opt into mutability.

Declaring a variable

let name = "Alice";

That’s it. No type annotation needed here — Rust infers &str from the value.

What happens if you try to reassign?

let x = 5;
x = 10;  // ❌ compiler error: cannot assign twice to immutable variable

The compiler stops you immediately. This isn’t a runtime crash or a warning — it’s a hard error.

How is this different from Java’s final?

In Java, you write final when you remember to. It’s opt-in, and most codebases are inconsistent about it. In Rust, immutability is the default, and mutability is the exception you explicitly declare.

// Java — mutable by accident
String name = "Alice";
name = "Bob";  // compiles fine, no warning
// Rust — immutable unless you say otherwise
let name = "Alice";
name = "Bob";  // ❌ won't compile

Shadowing: redeclaring with let

Rust lets you redeclare the same variable name with a new let. This is called shadowing:

let x = 5;
let x = x + 1;   // shadows the previous x
let x = x * 2;   // shadows again

println!("{}", x);  // 12

This is different from mutation — each let creates a new binding. You can even change the type this way, which you can’t do with mutation.