Coming from JavaScript to Rust? Here’s a practical guide to help you make the transition.

Why Rust?

Rust offers:

  • Memory safety without garbage collection
  • Performance comparable to C/C++
  • Modern tooling and package management
  • WebAssembly support for web development
  • Growing ecosystem with active community

Key Differences

1. Ownership System

Rust’s ownership system is unique:

// Rust: Ownership
let s1 = String::from("hello");
let s2 = s1; // s1 is moved to s2
// println!("{}", s1); // Error: s1 is no longer valid

// JavaScript: Reference
let s1 = "hello";
let s2 = s1; // s1 is still valid
console.log(s1); // Works fine

2. Mutability

Rust requires explicit mutability:

// Rust
let x = 5; // Immutable
let mut y = 10; // Mutable
y = 20; // OK

// JavaScript
let x = 5; // Mutable by default
x = 10; // OK
const y = 10; // Immutable

3. Type System

Rust has a strong, static type system:

// Rust: Explicit types
let x: i32 = 42;
let name: String = String::from("John");

// JavaScript: Dynamic types
let x = 42;
let name = "John";

Common Patterns

Variables and Mutability

// Immutable variable
let x = 5;

// Mutable variable
let mut y = 10;
y = 20;

// Constants
const MAX_POINTS: u32 = 100_000;

Functions

// Rust function
fn add(x: i32, y: i32) -> i32 {
    x + y // No semicolon = return value
}

// With explicit return
fn subtract(x: i32, y: i32) -> i32 {
    return x - y;
}

// JavaScript equivalent
function add(x, y) {
    return x + y;
}

Structs (Objects)

// Rust struct
struct User {
    name: String,
    age: u32,
    email: String,
}

impl User {
    fn new(name: String, age: u32, email: String) -> Self {
        User { name, age, email }
    }
    
    fn greet(&self) {
        println!("Hello, I'm {}", self.name);
    }
}

// Usage
let user = User::new(
    String::from("John"),
    30,
    String::from("[email protected]"),
);
user.greet();

Enums and Pattern Matching

// Rust enum
enum Status {
    Pending,
    Approved,
    Rejected,
}

// Pattern matching
fn handle_status(status: Status) {
    match status {
        Status::Pending => println!("Processing..."),
        Status::Approved => println!("Approved!"),
        Status::Rejected => println!("Rejected."),
    }
}

// Enum with data
enum Result<T, E> {
    Ok(T),
    Err(E),
}

Error Handling

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

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

// With ? operator
fn calculate() -> Result<f64, String> {
    let result = divide(10.0, 2.0)?;
    Ok(result * 2.0)
}

Collections

// Vector (array)
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
vec.push(3);

// Or with macro
let vec = vec![1, 2, 3];

// HashMap
use std::collections::HashMap;

let mut map = HashMap::new();
map.insert("key1", "value1");
map.insert("key2", "value2");

Iterators

// Rust iterators
let numbers = vec![1, 2, 3, 4, 5];

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

// JavaScript equivalent
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers
    .map(x => x * 2)
    .filter(x => x > 5);

Web Development with Rust

WebAssembly

Compile Rust to WebAssembly:

// src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[wasm_bindgen]
pub struct Calculator {
    value: i32,
}

#[wasm_bindgen]
impl Calculator {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Calculator {
        Calculator { value: 0 }
    }
    
    #[wasm_bindgen]
    pub fn add(&mut self, n: i32) {
        self.value += n;
    }
    
    #[wasm_bindgen]
    pub fn get_value(&self) -> i32 {
        self.value
    }
}

Web Frameworks

Popular Rust web frameworks:

  • Actix Web: High-performance, actor-based
  • Rocket: Easy to use, type-safe
  • Axum: Modern, async-first
  • Warp: Lightweight, functional

Learning Path

1. Start with Basics

  • Variables and types
  • Functions and control flow
  • Ownership and borrowing
  • Structs and enums

2. Practice with Projects

  • Command-line tools
  • Web servers
  • WebAssembly modules
  • System utilities

3. Learn Advanced Concepts

  • Lifetimes
  • Traits and generics
  • Async/await
  • Macros

Common Pitfalls

1. Fighting the Borrow Checker

// Common error
let s = String::from("hello");
let r1 = &s;
let r2 = &s; // OK: multiple immutable references
let r3 = &mut s; // Error: can't have mutable and immutable references

2. Moving Values

// Error: value moved
let s1 = String::from("hello");
let s2 = s1;
println!("{}", s1); // Error: s1 was moved

// Solution: Clone
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{}", s1); // OK

3. Lifetime Annotations

// Lifetime annotation needed
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

Resources

Official

Tools

  • cargo - Package manager
  • rustfmt - Code formatter
  • clippy - Linter
  • rust-analyzer - Language server

Conclusion

Rust offers:

  • Safety: Memory safety without GC
  • Performance: Near C/C++ speed
  • Modern: Great tooling and ecosystem
  • Versatile: Systems programming to web

The learning curve is steep, but the benefits are worth it:

  • Fewer bugs
  • Better performance
  • Modern tooling
  • Growing ecosystem

Start small, practice regularly, and don’t fight the borrow checker—work with it!

Happy Rusting! 🦀