Variables and Mutability
One of the most noticeable differences between Rust and JavaScript is how variables and mutability work. Let’s explore these differences to help you transition between the two languages.
Variables in JavaScript
Section titled “Variables in JavaScript”In JavaScript, you can declare variables with var
, let
, or const
:
// Variable declarations in JavaScript
var oldSchool = "I'm hoisted";
let mutable = "I can be changed";
const immutable = "I cannot be reassigned";
// Reassignment
mutable = "New value"; // ✅ Works
// immutable = "New value"; // ❌ TypeError: Assignment to constant variable
// But objects declared with const CAN be modified
const person = { name: "Alice" };
person.name = "Bob"; // ✅ Works! Only the binding is immutable
Variables in Rust
Section titled “Variables in Rust”Rust takes a different approach, making variables immutable by default:
// Variable declarations in Rust
let immutable = "I cannot be changed"; // Immutable by default
let mut mutable = "I can be changed"; // Use 'mut' to make mutable
// Reassignment
// immutable = "New value"; // ❌ Error: cannot assign twice to immutable variable
mutable = "New value"; // ✅ Works
Key Differences
Section titled “Key Differences”- In JavaScript, variables declared with
let
are mutable by default - In Rust, variables are immutable by default and require
mut
for mutability - JavaScript’s
const
prevents reassignment but not modification of object properties - In Rust, immutability is more comprehensive (we’ll see more when we get to references)
Constants
Section titled “Constants”Both languages have constants, but they work differently:
JavaScript Constants
Section titled “JavaScript Constants”const PI = 3.14159;
// PI = 3.0; // ❌ Error: Assignment to constant variable
// But objects can be modified
const user = { name: "Alice" };
user.name = "Bob"; // ✅ Works - only the binding is constant, not the content
Rust Constants
Section titled “Rust Constants”const PI: f64 = 3.14159;
// PI = 3.0; // ❌ Error: cannot assign to this expression
// Constants require type annotations and use SCREAMING_SNAKE_CASE by convention
const MAX_POINTS: u32 = 100_000;
Rust constants:
- Must have explicit type annotations
- Can only be set to a constant expression (evaluated at compile time)
- Can be declared in any scope, including the global scope
- Are inlined at compile time
Shadowing in Rust
Section titled “Shadowing in Rust”Rust allows “shadowing” of variables, which is declaring a new variable with the same name as a previous variable:
let x = 5;
let x = x + 1; // Shadows the previous 'x'
let x = x * 2; // Shadows again
println!("The value of x is: {}", x); // 12
This is different from mutation:
// With shadowing
let spaces = " ";
let spaces = spaces.len(); // ✅ Works, type can change with shadowing
// With mutation
let mut spaces = " ";
// spaces = spaces.len(); // ❌ Error: mismatched types
JavaScript Variable Shadowing
Section titled “JavaScript Variable Shadowing”JavaScript has similar behavior with block-scoped variables:
let x = 5;
{
let x = x + 1; // Shadows the outer 'x'
console.log(x); // 6
}
console.log(x); // 5
However, you can’t redeclare the same variable in the same scope in JavaScript:
let x = 10;
let x = 20; // ❌ SyntaxError: Identifier 'x' has already been declared
Variable Scope
Section titled “Variable Scope”Both languages use curly braces to define scopes:
JavaScript
Section titled “JavaScript”{
let x = 10;
console.log(x); // 10
}
// console.log(x); // ❌ ReferenceError: x is not defined
{
let x = 10;
println!("x: {}", x); // 10
}
// println!("x: {}", x); // ❌ Error: cannot find value `x` in this scope
Naming Conventions
Section titled “Naming Conventions”JavaScript
Section titled “JavaScript”camelCase
for variables and functionsPascalCase
for classes and constructor functionsSCREAMING_SNAKE_CASE
for constants
snake_case
for variables and functionsPascalCase
for types and traitsSCREAMING_SNAKE_CASE
for constants and static variables
Type Inference
Section titled “Type Inference”Both Rust and JavaScript can infer types, but in very different ways:
JavaScript (Dynamic Typing)
Section titled “JavaScript (Dynamic Typing)”let x = 5; // Number
x = "hello"; // Now it's a String - no problem!
Rust (Static Typing with Inference)
Section titled “Rust (Static Typing with Inference)”let x = 5; // Inferred as i32
// x = "hello"; // ❌ Error: mismatched types
// Need explicit type annotation for type changes
let x = 5; // i32
let x: &str = "hello"; // Shadowing with a new type
Destructuring
Section titled “Destructuring”Both languages support destructuring:
JavaScript
Section titled “JavaScript”const point = { x: 10, y: 20 };
const { x, y } = point;
console.log(x, y); // 10 20
const numbers = [1, 2, 3];
const [first, second] = numbers;
console.log(first, second); // 1 2
struct Point {
x: i32,
y: i32,
}
let point = Point { x: 10, y: 20 };
let Point { x, y } = point;
println!("x: {}, y: {}", x, y); // x: 10, y: 20
let numbers = [1, 2, 3];
let [first, second, _] = numbers;
println!("first: {}, second: {}", first, second); // first: 1, second: 2
Best Practices
Section titled “Best Practices”JavaScript
Section titled “JavaScript”- Use
const
by default,let
when you need to reassign - Avoid
var
in modern JavaScript - Use meaningful variable names
- Keep variables close to where they’re used
- Use immutable variables by default
- Only use
mut
when you need to change a value - Use shadowing for type changes or when you want to reuse a name
- Follow Rust naming conventions
- Keep variables in the smallest scope possible
Memory Considerations
Section titled “Memory Considerations”An important difference that isn’t immediately visible is how these variables are stored in memory:
JavaScript
Section titled “JavaScript”JavaScript handles memory allocation and garbage collection automatically. You generally don’t need to worry about where values live.
In Rust, values can be allocated on the stack or heap, and the ownership system (which we’ll cover later) determines how memory is managed.
// Stack allocated
let x = 5;
// Heap allocated, but Rust abstracts memory management
let s = String::from("hello");
Next Steps
Section titled “Next Steps”Now that you understand variables and mutability in Rust, let’s move on to Data Types to learn about Rust’s rich type system.