Skip to content
free · open source · written for TS/JS developers

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.

// one_problem_two_languages familiar control flow, explicit error types
user.tsuser.rs
typescript// familiar control flow
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();
}
rust// failure stays in the return type
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?)
}
31Guide sections
6Capstone projects
2024Rust edition
MITOpen source

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.

01

ownership_and_borrowing/

JavaScript relies on garbage collection. Rust uses ownership and borrowing to make data lifetime and mutation rules explicit at compile time.

[ownership][borrow][lifetimes][drop]
ownership.rs
let s = String::from("hello");
let s2 = s; // ownership moves to s2
// println!("{s}"); // compile error: s no longer valid
println!("{s2}"); // ok: s2 owns the string
02

result_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.

[result][option][question_mark][error_types]
errors.rs
fn parse_double(input: &str) -> Result<i32, ParseIntError> {
let n: i32 = input.trim().parse()?; // ? propagates the error
Ok(n * 2)
}
03

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.

[future][tokio][tasks][channels]
async.rs
#[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:?}");
}
language/00–15

Setup and syntax lead into ownership, collections, errors, traits, async, testing, macros, and serialization.

shipping/25–30

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.

The project section also includes a WASM Game of Life and a production-shaped URL shortener. Browse all six projects →

It explains the intended audience, prerequisites, suggested routes, and how to use the TS/JS comparisons without treating them as exact equivalents.

github.com/zeybek/rs4ts.dev · MIT licensed · no analytics