Rust variables vs JavaScript variables: A comparison
Rust is a systems programming language designed for performance, reliability, and safety - particularly focusing on memory safety without needing a garbage collector.
It's becoming increasingly popular in the developer community, and importantly for us as developers of serverless cloud applications, Rust runs on AWS Lambda. Not as a supported runtime like Python or Node.js - but because Rust compiles to native code, you don't need a dedicated runtime to run Rust code on Lambda. It's therefore super fast compared to Node.js, and results in quicker, cheaper invocations. There are also helpful tools for Rust on Lambda, like the awslabs/aws-lambda-rust-runtime repo and Cargo Lambda.
Recently we've been looking at using Rust as an alternative to Node.js for writing Lambda functions and as a language to write cross-platform desktop applications. However learning a new language as "old dog" developers has been a challenge, and we've been doing a lot of learning - often comparing the syntax of Rust to our familar languages of JavaScript and TypeScript.
In this article we've compiled a guide to Rust variables and how their syntax and use differs from JavaScript. Hopefully this helps to cement this knowledge in our own minds!
1. Immutability by Default
In Rust, variables are immutable by default, which means once you assign a value to a variable, you cannot change it unless you explicitly declare it as mutable using mut
.
Rust Example:
let x = 5;
// x = 6; // This will cause a compiler error because x is immutable.
let mut y = 10; // Declaring a mutable variable
y = 15; // Now you can reassign a value to y
In contrast, JavaScript variables are mutable by default when declared with var
or let
. The const
keyword in JS makes variables immutable.
JavaScript Example:
let x = 5;
x = 6; // No error, as variables declared with `let` are mutable.
const y = 10;
// y = 15; // This will cause an error because y is immutable (declared with `const`).
2. Type Inference
Rust is statically typed, but it has type inference. The compiler can infer the type of a variable from the value assigned, although you can specify types explicitly if needed.
Rust Example:
let a = 42; // Rust infers that 'a' is of type i32 (default integer type).
let b: f64 = 3.14; // Explicitly declaring 'b' as a floating-point number (f64).
JS is dynamically typed, meaning that the type of a variable is
determined at runtime, and you can change the type of a variable on the fly.
JavaScript Example:
let a = 42; // Initially, 'a' is a number.
a = "hello"; // In JavaScript, you can change 'a' to a string without an error.
3. Shadowing
Rust allows shadowing, which means you can declare a new variable with the same name in the same scope. The new variable "shadows" the old one, and it can even have a different type.
Rust Example:
let x = 5;
let x = x + 1; // Shadows the old 'x'. The new 'x' is 6.
let x = "text"; // Shadows again. 'x' is now a string.
In JS, shadowing can be done within different scopes (e.g., inside functions or blocks), but redeclaring variables within the same block is not allowed unless you're using var
, which has function-level scope.
JavaScript Example:
let x = 5;
{
let x = 10; // This is a new 'x' scoped inside the block.
console.log(x); // 10
}
console.log(x); // 5 (the outer 'x' is unaffected)
While both languages support shadowing within scopes, Rust can shadow variables in the same scope with new types, while JS does not allow re-declaring let
or const
variables within the same scope.
4. Constants
Constants in Rust are declared using the const
keyword. They must have a type annotation and must be assigned a value that can be computed at compile time. Constants are always immutable.
Rust Example:
const MAX_POINTS: u32 = 100_000;
In JS, const
is used for immutable variables, but it doesn't behave exactly like Rust's const
. In JS, a const
can still reference a mutable object whose properties can still change, while in Rust, the value must be fully immutable.
JavaScript Example:
const MAX_POINTS = 100000;
// const with objects
const obj = { points: 1000 };
obj.points = 2000; // Allowed, because the object is mutable even if the variable is constant.
5. Ownership and Borrowing
One of Rust's interesting features is its ownership model, which ensures memory safety without needing a garbage collector. When you assign a variable to another variable, ownership is moved, not copied, by default for complex types, and the original variable becomes invalid unless explicitly cloned.
Rust Example:
let s1 = String::from("hello");
let s2 = s1; // Ownership is moved. s1 is no longer valid.
// println!("{}", s1); // This will cause a compile-time error.
let s3 = s2.clone(); // Explicitly cloning s2 to s3.
JS doesn't have an ownership model. When assigning complex types like objects or arrays, the variable gets a reference to the same object in memory, and changes to one variable affect the other.
JavaScript Example:
let s1 = { greeting: "hello" };
let s2 = s1; // s2 references the same object as s1.
s2.greeting = "hi";
console.log(s1.greeting); // "hi" (both s1 and s2 point to the same object).
Rust enforces strict ownership rules to avoid memory leaks, while JavaScript uses references for complex types and relies on a garbage collector for memory management.
6. Variable Lifetimes and Scoping
Variables in Rust have strict lifetimes based on their scope. A variable's memory is freed automatically when it goes out of scope, and Rust's ownership model ensures memory safety without needing garbage collection.
Rust Example:
{
let x = 5; // x is valid within this block
} // x goes out of scope and is dropped.
Variables declared with let
and const
are block-scoped (like in Rust), while variables declared with var
are function-scoped. JS uses a garbage collector to automatically manage memory.
JavaScript Example:
{
let x = 5; // x is block-scoped and only available inside this block
}
// x is no longer available here.
Rust uses deterministic memory management so variables are dropped as soon as they go out of scope, while JavaScript relies on a garbage collector, which may free memory later.
Summary of key differences:
- Immutability: Rust is immutable by default, while JS is mutable unless
const
is used. - Type System: Rust is statically typed with type inference; JS is dynamically typed.
- Shadowing: Rust allows shadowing within the same scope with new types, while JS doesn't.
- Constants: Rust
const
values are fully immutable and must be known at compile time; JSconst
allows mutable objects. - Ownership: Rust has strict ownership and borrowing rules for memory safety; JS uses references and garbage collection.
- Memory Management: Rust's memory management is deterministic based on scope; JS uses a garbage collector.