Learn Rust from the TS/JS you know
This guide assumes you already write TypeScript or JavaScript. It uses familiar code to explain ownership, error handling, traits, async, and the Rust ecosystem, including the points where Rust needs a different approach.
type User = { id: string; name: string };
async function getUser(id: string): Promise<User> { const response = await fetch("/api/users/" + id); if (!response.ok) throw new Error("request failed"); return response.json();}struct User { id: String, name: String }
async fn get_user(id: &str) -> Result<User, ApiError> { let url = format!("/api/users/{id}"); let response = reqwest::get(url).await?.error_for_status()?; Ok(response.json().await?)}how the guide is organized
Section titled “how the guide is organized”Start from familiar code
Chapters put familiar TS/JS code beside its Rust equivalent, then explain the rules behind the differences.
Compare semantics, not just syntax
The guide marks places where familiar syntax behaves differently, especially around ownership, errors, async execution, and type modeling.
Read errors with the working code
Compiler messages and common mistakes are explained beside the corrected version, so the rejected code has context.
Practice beyond isolated snippets
Exercises reinforce individual topics. Six dependency-locked capstone crates combine them in complete programs checked by CI.
three differences to understand first
Section titled “three differences to understand first”ownership_and_borrowing/
JavaScript relies on garbage collection. Rust uses ownership and borrowing to make data lifetime and mutation rules explicit at compile time.
let s = String::from("hello");let s2 = s; // ownership moves to s2// println!("{s}"); // compile error: s no longer validprintln!("{s2}"); // ok: s2 owns the stringresult_and_option/
For recoverable failures, Rust returns Result<T, E>; missing values are Option<T>. The ? operator propagates errors without hiding them from the function signature.
fn parse_double(input: &str) -> Result<i32, ParseIntError> { let n: i32 = input.trim().parse()?; // ? propagates the error Ok(n * 2)}async_and_concurrency/
The async/await syntax is familiar, but Rust futures are lazy and need an executor such as Tokio. Async coordinates waiting; CPU-bound parallelism is a separate choice.
#[tokio::main]async fn main() { // Tokio's runtime drives both futures concurrently let (a, b) = tokio::join!(fetch_user(1), fetch_user(2)); println!("{a:?} {b:?}");}one guide, three stages
Section titled “one guide, three stages”Setup and syntax lead into ownership, collections, errors, traits, async, testing, macros, and serialization.
Build with Axum, SQLx, CLI tooling, WebAssembly, FFI, performance tools, common patterns, and the Rust ecosystem.
Continue with advanced topics, systems programming, security, production operations, migration, and the capstone projects.
The chapter order is the default route, not a requirement. Use the sidebar to open a topic directly when a project gives you a concrete question.
// capstone_projectsuse Rust in complete programs
Section titled “use Rust in complete programs”The project section also includes a WASM Game of Life and a production-shaped URL shortener. Browse all six projects →
start with the introduction
Section titled “start with the introduction”It explains the intended audience, prerequisites, suggested routes, and how to use the TS/JS comparisons without treating them as exact equivalents.