DEV Community

Cover image for Rust πŸ¦€ for JavaScript Developers: A Super Simple Guide.
Mohamed Ismail
Mohamed Ismail

Posted on • Edited on

Rust πŸ¦€ for JavaScript Developers: A Super Simple Guide.

Coming from JavaScript? This guide maps familiar JS concepts to their Rust equivalents, helping you understand the differences in approach and syntax.

Rust prioritizes safety and performance over flexibility. Where JavaScript lets you wing it, Rust makes you be explicit. The payoff is faster, more reliable code.

Setup and Tooling

Task JavaScript Rust
Runtime Node.js Native executable
Package manager npm / yarn cargo
Initialize project npm init cargo new project_name
Install dependency npm install lodash cargo add serde
Run code node index.js cargo run
Build for production Often not needed cargo build --release

Quick Start

JavaScript:

mkdir my-project
cd my-project
npm init -y
npm install express
node server.js
Enter fullscreen mode Exit fullscreen mode

Rust:

cargo new my-project
cd my-project
cargo add tokio --features full
cargo run
Enter fullscreen mode Exit fullscreen mode

Project Structure

my-rust-project/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ main.rs          # Entry point
β”‚   β”œβ”€β”€ lib.rs           # Library root (if applicable)
β”‚   └── utils.rs         # Additional modules
β”œβ”€β”€ Cargo.toml           # Dependencies and metadata
└── target/              # Build artifacts (like node_modules)
Enter fullscreen mode Exit fullscreen mode

Variables and Mutability

JavaScript variables are mutable by default. Rust variables are immutable unless explicitly marked otherwise.

JavaScript:

let name = "Alice";
name = "Bob";           // Works fine

const age = 25;
// age = 26;            // TypeError
Enter fullscreen mode Exit fullscreen mode

Rust:

fn main() {
    let name = "Alice";
    // name = "Bob";     // Compile error

    let mut age = 25;    // mut = mutable
    age = 26;            // Now this works

    // Variable shadowing (different from mutation)
    let score = 100;     // i32
    let score = "high";  // &str - new variable, same name
}
Enter fullscreen mode Exit fullscreen mode

Data Types

JavaScript has loose typing. Rust has strict, explicit types.

JavaScript Rust Example
number i32, f64, u32 let x: i32 = 42;
string String, &str let s = String::from("hello");
boolean bool let flag = true;
Array Vec<T>, [T; N] let nums = vec![1, 2, 3];
Object struct, HashMap See structs section

String Handling

JavaScript:

let greeting = "Hello";
let name = "World";
let message = `${greeting}, ${name}!`;
console.log(message.length);
console.log(message.toUpperCase());
Enter fullscreen mode Exit fullscreen mode

Rust:

fn main() {
    let greeting = "Hello";              // &str (string slice)
    let name = String::from("World");    // String (owned)
    let message = format!("{}, {}!", greeting, name);

    println!("Length: {}", message.len());
    println!("Uppercase: {}", message.to_uppercase());

    // Modifying strings
    let mut dynamic = String::from("Hello");
    dynamic.push_str(", World!");
    println!("{}", dynamic);
}
Enter fullscreen mode Exit fullscreen mode

Functions

JavaScript functions are flexible with parameters and return types. Rust requires explicit types.

JavaScript:

function add(a, b) {
    return a + b;
}

const greet = (name = "Guest") => `Hello, ${name}!`;

console.log(add(2, 3));
console.log(greet());
Enter fullscreen mode Exit fullscreen mode

Rust:

fn add(a: i32, b: i32) -> i32 {
    a + b  // No semicolon = implicit return
}

fn greet(name: Option<&str>) -> String {
    match name {
        Some(n) => format!("Hello, {}!", n),
        None => "Hello, Guest!".to_string(),
    }
}

fn main() {
    println!("{}", add(2, 3));
    println!("{}", greet(Some("Alice")));
    println!("{}", greet(None));
}
Enter fullscreen mode Exit fullscreen mode

Collections

Arrays and Vectors

JavaScript:

let numbers = [1, 2, 3];
numbers.push(4);
let doubled = numbers.map(x => x * 2);
console.log(doubled);
Enter fullscreen mode Exit fullscreen mode

Rust:

fn main() {
    let mut numbers = vec![1, 2, 3];  // Vec<i32>
    numbers.push(4);

    let doubled: Vec<i32> = numbers
        .iter()
        .map(|x| x * 2)
        .collect();

    println!("{:?}", doubled);  // [2, 4, 6, 8]
}
Enter fullscreen mode Exit fullscreen mode

Objects vs Structs

JavaScript:

let user = {
    name: "Alice",
    age: 30
};

user.city = "New York";  // Dynamic property
console.log(user.name);
Enter fullscreen mode Exit fullscreen mode

Rust:

#[derive(Debug)]
struct User {
    name: String,
    age: u32,
}

fn main() {
    let mut user = User {
        name: String::from("Alice"),
        age: 30,
    };

    // user.city = "New York";  // Can't add fields dynamically
    user.age = 31;  // But can modify existing fields

    println!("{:?}", user);
}
Enter fullscreen mode Exit fullscreen mode

Hash Maps (Like JS Objects)

JavaScript:

let userMap = {
    name: "Alice",
    city: "New York"
};
console.log(userMap.name);
Enter fullscreen mode Exit fullscreen mode

Rust:

use std::collections::HashMap;

fn main() {
    let mut user_map = HashMap::new();
    user_map.insert("name", "Alice");
    user_map.insert("city", "New York");

    println!("{:?}", user_map.get("name"));  // Some("Alice")
}
Enter fullscreen mode Exit fullscreen mode

Error Handling

JavaScript uses try/catch. Rust uses Result and Option types.

JavaScript:

function divide(a, b) {
    if (b === 0) {
        throw new Error("Division by zero");
    }
    return a / b;
}

try {
    console.log(divide(10, 2));
    console.log(divide(10, 0));
} catch (error) {
    console.log("Error:", error.message);
}
Enter fullscreen mode Exit fullscreen mode

Rust:

fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err("Division by zero".to_string())
    } else {
        Ok(a / b)
    }
}

fn main() {
    match divide(10.0, 2.0) {
        Ok(result) => println!("Result: {}", result),
        Err(error) => println!("Error: {}", error),
    }

    // Alternative: using unwrap_or
    let result = divide(10.0, 0.0).unwrap_or(-1.0);
    println!("Result with default: {}", result);
}
Enter fullscreen mode Exit fullscreen mode

Control Flow

Conditionals

JavaScript:

const score = 85;
let grade;

if (score >= 90) {
    grade = "A";
} else if (score >= 80) {
    grade = "B";
} else {
    grade = "F";
}

console.log(`Grade: ${grade}`);
Enter fullscreen mode Exit fullscreen mode

Rust:

fn main() {
    let score = 85;

    let grade = if score >= 90 {
        "A"
    } else if score >= 80 {
        "B"
    } else {
        "F"
    };

    println!("Grade: {}", grade);
}
Enter fullscreen mode Exit fullscreen mode

Pattern Matching

JavaScript uses switch statements. Rust has powerful match expressions.

JavaScript:

const day = "Monday";

switch (day) {
    case "Monday":
        console.log("Start of work week");
        break;
    case "Friday":
        console.log("TGIF!");
        break;
    default:
        console.log("Regular day");
}
Enter fullscreen mode Exit fullscreen mode

Rust:

fn main() {
    let day = "Monday";

    match day {
        "Monday" => println!("Start of work week"),
        "Friday" => println!("TGIF!"),
        _ => println!("Regular day"),
    }

    // Match with multiple values
    match day {
        "Saturday" | "Sunday" => println!("Weekend!"),
        _ => println!("Weekday"),
    }
}
Enter fullscreen mode Exit fullscreen mode

Loops

JavaScript:

// For loop
for (let i = 0; i < 5; i++) {
    console.log(i);
}

// For...of loop
const items = [1, 2, 3];
for (const item of items) {
    console.log(item);
}

// While loop
let count = 0;
while (count < 3) {
    console.log(count);
    count++;
}
Enter fullscreen mode Exit fullscreen mode

Rust:

fn main() {
    // Range loop
    for i in 0..5 {
        println!("{}", i);
    }

    // Iterating over collection
    let items = vec![1, 2, 3];
    for item in &items {
        println!("{}", item);
    }

    // While loop
    let mut count = 0;
    while count < 3 {
        println!("{}", count);
        count += 1;
    }

    // Infinite loop
    let mut counter = 0;
    loop {
        if counter >= 3 {
            break;
        }
        println!("{}", counter);
        counter += 1;
    }
}
Enter fullscreen mode Exit fullscreen mode

Modules and Organization

JavaScript:

// math.js
export function add(a, b) {
    return a + b;
}

export function subtract(a, b) {
    return a - b;
}

// main.js
import { add, subtract } from './math.js';
console.log(add(5, 3));
Enter fullscreen mode Exit fullscreen mode

Rust:

// src/math.rs
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

pub fn subtract(a: i32, b: i32) -> i32 {
    a - b
}

// src/main.rs
mod math;  // Declares the module

fn main() {
    println!("{}", math::add(5, 3));

    // Or import specific functions
    use math::subtract;
    println!("{}", subtract(5, 3));
}
Enter fullscreen mode Exit fullscreen mode

Memory Management

JavaScript has garbage collection. Rust has ownership.

JavaScript (Garbage Collected):

function createArray() {
    let data = new Array(1000).fill(0);
    return data;
}

let myArray = createArray();
// Memory automatically cleaned up when myArray goes out of scope
Enter fullscreen mode Exit fullscreen mode

Rust (Ownership):

fn create_vector() -> Vec<i32> {
    let data = vec![0; 1000];
    data  // Ownership transferred to caller
}

fn main() {
    let my_vector = create_vector();
    println!("Length: {}", my_vector.len());
    // Memory automatically cleaned up when my_vector goes out of scope
}
Enter fullscreen mode Exit fullscreen mode

Common Patterns

Null Safety

JavaScript:

function getUserName(user) {
    return user?.name || "Unknown";
}

const user1 = { name: "Alice" };
const user2 = null;

console.log(getUserName(user1));  // "Alice"
console.log(getUserName(user2));  // "Unknown"
Enter fullscreen mode Exit fullscreen mode

Rust:

struct User {
    name: String,
}

fn get_user_name(user: Option<User>) -> String {
    match user {
        Some(u) => u.name,
        None => "Unknown".to_string(),
    }
}

fn main() {
    let user1 = Some(User { name: "Alice".to_string() });
    let user2 = None;

    println!("{}", get_user_name(user1));  // "Alice"
    println!("{}", get_user_name(user2));  // "Unknown"
}
Enter fullscreen mode Exit fullscreen mode

Async Programming

JavaScript:

async function fetchUser(id) {
    const response = await fetch(`/api/users/${id}`);
    return response.json();
}

fetchUser(1)
    .then(user => console.log(user))
    .catch(error => console.error(error));
Enter fullscreen mode Exit fullscreen mode

Rust:

use reqwest;

async fn fetch_user(id: u32) -> Result<String, reqwest::Error> {
    let response = reqwest::get(&format!("https://api.example.com/users/{}", id))
        .await?
        .text()
        .await?;
    Ok(response)
}

#[tokio::main]
async fn main() {
    match fetch_user(1).await {
        Ok(user) => println!("{}", user),
        Err(error) => eprintln!("Error: {}", error),
    }
}
Enter fullscreen mode Exit fullscreen mode

Key Differences Summary

  1. Compilation: Rust compiles to native code, JavaScript is interpreted
  2. Type System: Rust has strict static typing, JavaScript has dynamic typing
  3. Memory Management: Rust uses ownership, JavaScript uses garbage collection
  4. Error Handling: Rust uses Result/Option, JavaScript uses exceptions
  5. Mutability: Rust variables are immutable by default, JavaScript variables are mutable
  6. Performance: Rust is typically much faster, JavaScript prioritizes development speed

Getting Comfortable

Start by rewriting simple JavaScript functions in Rust. The compiler will guide you through the differences. Don't fight the borrow checker - learn what it's trying to teach you about memory safety.

The learning curve is steep initially, but the benefits in performance and reliability make it worthwhile for systems programming, web backends, and anywhere speed matters.

Top comments (2)

Collapse
 
mojomagic profile image
MOJO • Edited

amazing article

Some comments may only be visible to logged-in visitors. Sign in to view all comments.