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
Rust:
cargo new my-project
cd my-project
cargo add tokio --features full
cargo run
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)
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
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
}
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());
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);
}
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());
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));
}
Collections
Arrays and Vectors
JavaScript:
let numbers = [1, 2, 3];
numbers.push(4);
let doubled = numbers.map(x => x * 2);
console.log(doubled);
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]
}
Objects vs Structs
JavaScript:
let user = {
name: "Alice",
age: 30
};
user.city = "New York"; // Dynamic property
console.log(user.name);
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);
}
Hash Maps (Like JS Objects)
JavaScript:
let userMap = {
name: "Alice",
city: "New York"
};
console.log(userMap.name);
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")
}
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);
}
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);
}
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}`);
Rust:
fn main() {
let score = 85;
let grade = if score >= 90 {
"A"
} else if score >= 80 {
"B"
} else {
"F"
};
println!("Grade: {}", grade);
}
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");
}
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"),
}
}
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++;
}
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;
}
}
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));
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));
}
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
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
}
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"
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"
}
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));
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),
}
}
Key Differences Summary
- Compilation: Rust compiles to native code, JavaScript is interpreted
- Type System: Rust has strict static typing, JavaScript has dynamic typing
- Memory Management: Rust uses ownership, JavaScript uses garbage collection
-
Error Handling: Rust uses
Result
/Option
, JavaScript uses exceptions - Mutability: Rust variables are immutable by default, JavaScript variables are mutable
- 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)
amazing article
Some comments may only be visible to logged-in visitors. Sign in to view all comments.