Skip to main content
Tutorials

Rust for JavaScript Developers: First Steps

A gentle introduction to Rust's key concepts through the lens of JavaScript familiarity — ownership, borrowing, and why they matter.

Mbeah Essilfie

Mbeah Essilfie

July 5, 2026 at 06:03 AM

13 min read 455 views
Rust for JavaScript Developers: First Steps

Why Rust?

As a JavaScript developer, you might wonder why you'd learn a systems language. Here's why:

  • Tools like esbuild, swc, Turbopack, and Biome are written in Rust
  • WebAssembly opens Rust to the browser
  • Understanding memory management makes you a better developer in any language

Variables and Mutability

In JavaScript, let allows reassignment. In Rust, variables are immutable by default:

fn main() {
    let name = "Mbeah";      // immutable
    // name = "Essilfie";    // ❌ compile error!

    let mut age = 25;        // mutable (opt-in)
    age = 26;                // ✅ works
}
Immutability by default forces you to be intentional about state changes. This prevents accidental mutations — a common source of bugs in JavaScript.

Ownership: The Big Idea

Every value in Rust has exactly one owner. When the owner goes out of scope, the value is dropped (freed):

fn main() {
    let s1 = String::from("hello");
    let s2 = s1;  // s1's ownership MOVES to s2

    // println!("{}", s1); // ❌ error: s1 no longer valid
    println!("{}", s2);    // ✅ works
}

In JavaScript terms: imagine if assigning a variable to another made the original undefined. That's a move.

Borrowing: References Without Ownership

fn print_length(s: &String) {  // borrows s, doesn't own it
    println!("Length: {}", s.len());
}

fn main() {
    let message = String::from("hello");
    print_length(&message);    // lend it out
    println!("{}", message);   // still valid!
}

Rules:

  • You can have many &T (shared/read references)
  • OR one &mut T (exclusive/write reference)
  • Never both at the same time

Pattern Matching

Like a supercharged switch that the compiler verifies is exhaustive:

enum HttpStatus {
    Ok,
    NotFound,
    ServerError(String),
}

fn describe(status: HttpStatus) -> &'static str {
    match status {
        HttpStatus::Ok => "All good!",
        HttpStatus::NotFound => "Resource not found",
        HttpStatus::ServerError(msg) => {
            println!("Error: {}", msg);
            "Internal error"
        }
    }
}

Error Handling (No Exceptions!)

use std::fs;

fn read_config() -> Result<String, std::io::Error> {
    let content = fs::read_to_string("config.toml")?; // ? propagates errors
    Ok(content)
}

The ? operator is like a try/catch that returns early on error — but the compiler forces you to handle every error path.

Mapping JS Concepts to Rust

JavaScriptRust
let / constlet mut / let
null / undefinedOption<T>
try/catchResult<T, E>
interfacetrait
classstruct + impl
garbage collectionownership + borrowing

Start small — build a CLI tool, contribute to a Rust project, or try rewriting a hot path in WebAssembly.

JavaScriptRust
Mbeah Essilfie

Written by Mbeah Essilfie

Fullstack Software Developer

Read next

The Complete Guide to Vue 3 Composables
12 min read

The Complete Guide to Vue 3 Composables

Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.

Mbeah EssilfieMbeah Essilfie
988