In both JavaScript and Rust, const
, let
, and mut
define how variables are bound to values and whether those values can change. They may look similar, but they reflect very different design goals and runtime behaviors.
Before diving into let
and mut
, it’s worth clearing up a common misconception: Rust’s const
has nothing in common with JavaScript’s const
. Despite the shared keyword, the two concepts solve completely different problems.
const
in Rust vs. const
in JavaScript
In JavaScript, const
prevents reassignment but doesn’t make the underlying value immutable. The binding is fixed, not the data.
1const arr = [1, 2];
2arr.push(3); // ✅ allowed
3arr = [4, 5]; // ❌ TypeError
The variable name arr
can’t be reassigned, but the array itself can be mutated freely. This can lead to subtle issues in React, for example:
1const [items, setItems] = useState([1, 2]);
2
3items.push(3); // ⚠️ allowed but mutates state directly
4setItems(items); // React may not detect the change
JavaScript’s const
is a runtime binding. The language won’t stop you from mutating a value that was meant to stay constant.
In Rust, const
is an entirely different construct. It defines a compile-time constant, a value that is known before the program runs and is embedded directly into the compiled binary, not stored on the heap or the stack.
1const PI: f64 = 3.1415;
2
3fn main() {
4 let r = 2.0;
5 let area = PI * r * r;
6 println!("Area: {}", area);
7}
The compiler replaces every reference to PI
with its literal value during compilation. This makes it ideal for data that never changes: mathematical constants, fixed limits, or configuration values. If you need a value computed at runtime, you must use let
instead — it allocates memory (on the stack or heap) and exists only during execution.
let
in Rust vs. let
and const
in JavaScript
With const
out of the way, we can now compare how both languages handle variable bindings and mutability through let
.
In JavaScript:
let
allows reassigning and mutating the value.const
prevents reassignment but still allows mutating the underlying object or array.
In Rust:
let
creates an immutable binding, and the value itself is also immutable.let mut
allows both reassignment and internal mutation.
1let v = vec![1, 2];
2// v.push(3); // ❌ cannot borrow as mutable
3
4let mut v = vec![1, 2];
5v.push(3); // ✅ works
At this point, let mut
in Rust behaves similarly to let
in JavaScript — both allow mutation.
The difference is that Rust makes the choice explicit and opt-in, while JavaScript assumes mutability by default.
Bindings and values
A binding connects a name to a value, and a value is the actual data stored in memory. Mutability can apply to either, depending on the language.
Rust’s rule is clear: you must declare exactly where change is allowed, rather than assuming it everywhere. This explicitness helps the compiler enforce safety guarantees that prevent data races and unintended side effects.
In other words, Rust’s philosophy is immutability by default. You must opt in to mutation using mut
, making every state change intentional and visible to the compiler.
1let mut counter = 0;
2counter += 1;
This requirement improves safety, especially in concurrent or multithreaded code.
It also extends to references: you can borrow a value immutably (&
) or mutably (&mut
), but never both at the same time.
Side-by-side comparison
Scenario | JavaScript | Rust |
---|---|---|
Reassign a variable | let x = 1; x = 2; ✅ |
let mut x = 1; x = 2; ✅ |
Reassign a constant | const x = 1; x = 2; ❌ |
const X: i32 = 1; X = 2; ❌ |
Mutate a collection | const arr = []; arr.push(1); ✅ |
let mut v = vec![]; v.push(1); ✅ |
Constant stored in | runtime memory | embedded at compile time (no heap/stack) |
Default mutability | mutable | immutable |
Scope | block | block |
Conclusion: intent over convenience
JavaScript and Rust take opposite approaches to mutability. JavaScript values flexibility, allowing mutation freely unless explicitly restricted. Rust values clarity, requiring developers to state their intent whenever mutation is allowed.
In practice, let mut
in Rust feels close to JavaScript’s let, while Rust’s plain let
has no true equivalent in JavaScript. JavaScript’s const
comes closer, but it’s a looser guarantee, because it prevents reassignment yet allows mutating the underlying data.
Rust’s model is stricter and more explicit: let
means nothing can change, and let mut
means change is allowed. This clear separation makes mutability a conscious design decision, not an accident of syntax, and that’s where Rust truly stands apart.