blog.dopana

Back

For developers transitioning from JavaScript or TypeScript to Rust, the syntax looks superficially familiar (curly braces, let, arrow closures). However, beneath the surface lies a fundamentally different execution model and memory architecture.

Explain Like I’m 10: Rented Apartment vs Owning Real Estate#

  • JavaScript (The Managed Apartment): You move in, use electricity and water freely, and leave trash in the hallway. A background landlord (the Garbage Collector) walks around periodically to clean up whatever you forgot. If you access something missing, you get undefined or null.
  • Rust (The Precision Workshop): Every single tool has a designated peg on the wall and an explicit owner. You must account for how things are moved or borrowed. Nothing is cleaned up by a background cleaner—instead, items self-destruct the exact millisecond they leave their workspace room (compile-time deterministic destruction).
graph TD
    subgraph JavaScript ["JavaScript Runtime Model"]
        JSEval["Dynamic Types & Objects"] --> JSHeap["V8 Heap Memory"]
        JSHeap --> GC["Background Garbage Collector (Stops the world periodically)"]
        JSEval --> JSNull["null / undefined (Runtime TypeError)"]
    end
    subgraph Rust ["Rust Compile-Time Model"]
        RustEval["Static Strict Typing"] --> RustCheck["Borrow Checker & Type System"]
        RustCheck --> Bin["Direct Machine Code (Zero-cost, No GC, RAII deterministic drop)"]
        RustEval --> RustEnum["Option<T> & Result<T, E> (Compile-time verified)"]
    end

1. Variables and Mutability#

In JS, const prevents reassignment, but inner object properties can still mutate. In Rust, let is completely immutable by default—including deep data.

javascript.js
// JS: let is mutable, const prevents re-binding
let x = 10;
x = 20; // OK

const user = { name: "Alice" };
user.name = "Bob"; // OK! Object contents can mutate
javascript
rust.rs
// Rust: immutable by default, requires 'mut' for mutation
let x = 10;
// x = 20; // ❌ Error: cannot assign twice to immutable variable

let mut y = 10;
y = 20; // ✅ OK

struct User { name: String }
let user = User { name: String::from("Alice") };
// user.name = String::from("Bob"); // ❌ Error: cannot mutate immutable binding
rust

2. Functions and Implicit Returns#

In Rust, the last expression in a block without a semicolon is returned automatically.

javascript.js
// JavaScript
function add(a, b) {
    return a + b;
}
const multiply = (a, b) => a * b; // Arrow implicit return
javascript
rust.rs
// Rust: type annotations required; omitting ';' returns value
fn add(a: i32, b: i32) -> i32 {
    a + b // implicit return (no semicolon!)
}

// Closures
let multiply = |a: i32, b: i32| a * b;
rust

3. Objects & Classes vs Structs & Traits#

JavaScript uses Prototype/Class OOP with this. Rust separates state (struct) from behavior (impl) and interfaces (trait).

javascript.js
// JavaScript Class
class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }
    area() {
        return this.width * this.height;
    }
}
const rect = new Rectangle(10, 20);
console.log(rect.area());
javascript

4. null & undefined vs Option<T>#

JavaScript programs frequently crash at runtime with TypeError: Cannot read properties of undefined. Rust completely eliminates null at compile time using Option<T>.

graph LR
    JSNullCheck["JS: user?.address?.city"] -->|Undefined at runtime| Crash["Uncaught TypeError if missed"]
    RustOption["Rust: Option&lt;String&gt;"] -->|Compiler forces match / if let| SafeHandling["Guaranteed Safe at Compile Time"]
javascript.js
// JS: May return null or undefined
function findUser(id) {
    if (id === 1) return { name: "Alice" };
    return null;
}
const user = findUser(2);
// console.log(user.name); // ❌ Uncaught TypeError: Cannot read properties of null
javascript

5. Error Handling: try/catch vs Result<T, E>#

JavaScript uses exception throwing that jumps across stack frames unexpectedly. Rust treats errors as regular return values via Result<T, E>.

javascript.js
// JS: try / catch / throw
try {
    const data = JSON.parse(rawText);
    console.log(data);
} catch (err) {
    console.error("Failed to parse JSON:", err.message);
}
javascript
rust.rs
// Rust: Result<T, E> and the ? operator
fn parse_number(s: &str) -> Result<i32, std::num::ParseIntError> {
    let num = s.trim().parse::<i32>()?;
    Ok(num * 2)
}

fn main() {
    match parse_number("42") {
        Ok(val) => println!("Calculated: {val}"),
        Err(e) => eprintln!("Parse failed: {e}"),
    }
}
rust

Quick Reference Comparison Table#

ConceptJavaScript / TypeScriptRust
TypingDynamic (JS) / Structural (TS)Static, Nominal, Strongly Typed
MemoryGarbage Collector (V8)RAII Ownership & Borrow Checker
Missing Valuesnull and undefinedOption<T> (Some or None)
Error Handlingtry / catch / throwResult<T, E> with ? operator
Async ModelSingle-threaded Event Loop (Promise)Multi-threaded async runtime (Tokio / Future)
Package Managernpm / bun / pnpm (package.json)cargo (Cargo.toml)
Immutabilityconst (shallow only)let (deeply immutable by default)

Summary#

  • Rust’s syntax will feel familiar to JS/TS developers, but semantics are strictly enforced at compile time.
  • Immutability is the default in Rust, requiring explicit mut.
  • OOP classes are replaced with struct data structures and trait interfaces.
  • Option and Result replace null, undefined, and throw, turning runtime crashes into compile errors.

References#