| The language and the host |
core, alloc and std ↗ |
the same line drawn in Rust, between core and std |
| Strict mode |
Strict clippy: denying the panic, and the arithmetic that comes with it ↗ |
opt-in strictness in Rust turns runtime aborts into compile errors |
| Automatic semicolon insertion |
A block is an expression ↗ |
in Rust, one semicolon changes what a block returns |
| ECMAScript versions |
RFC 1054 — the method that renamed itself to promise less ↗ |
how a change reaches Rust's standard library through an RFC |
| Which features your Node has |
MSRV: the oldest compiler you promise to support ↗ |
the oldest compiler a Rust crate promises to support |
| Which features your Node has |
rustup: the rustc you run is not the compiler ↗ |
the compiler on your PATH may not be the one you think |
| Eight types |
The integer types ↗ |
twelve integer types in Rust, where JavaScript has two numeric types |
null and undefined |
Partial functions: why Option exists ↗ |
in Rust a function with no answer for some inputs returns an Option, so "no answer" is one of its answers and the caller has to handle it. |
null and undefined |
Null dereference ↗ |
what null costs in C, and what Rust uses instead. |
null and undefined |
Six kinds of zero ↗ |
where JavaScript has two kinds of missing, Rust lets you declare as many as the problem has. |
| Primitives and wrappers |
Extension traits ↗ |
Rust puts a new method behind the dot on str with a trait you declare and import, not by adding it to a prototype that every string shares. |
| BigInt |
Big integers: BigInt and Python's int ↗ |
big integers in Rust and Python, side by side: Rust's num::BigInt truncates like a JavaScript BigInt, where Python's // floors. |
| Values and references |
Rc: the clone that copies a pointer ↗ |
Rc::clone copies a pointer, as JavaScript assignment does. |
| Values and references |
Copy or move? Swap the value, keep the program ↗ |
in Rust the type decides between copy and move. |
| Errors around values and types |
Some and None: reading an Option ↗ |
in Rust a value that may be missing has the type Option, and a match that leaves out the None case does not compile (E0004): the check tsc makes in entry 14, made by the compiler on every such value. |
| Errors around values and types |
unwrap is a TODO you forgot to remove ↗ |
Rust's way to skip that check is unwrap(), which panics on None at run time, much as entry 3's [0] throws on null. |
| Lints around values and types |
What a float stores ↗ |
the compiler itself has use-isnan's check: rustc's invalid_nan_comparisons lint fires on x == f64::NAN and points to x.is_nan(). |
| Lints around values and types |
Lints around references ↗ |
the same kind of page for rustc and clippy: twenty-five warnings around references and pointers, and six mistakes no lint catches. |
| Lints around values and types |
Clippy: the groups, the settings, the commands ↗ |
clippy's lints come in groups whose levels are set in Cargo.toml; ESLint's core rules are switched on one at a time, as --rule does here. |
=== and == |
Comparing two numbers of different types ↗ |
in Rust, two number types cannot be compared until you convert one |
| Truthy and falsy |
Meet the bool ↗ |
in Rust there is no truthiness at all |
The + operator |
Concatenating strings ↗ |
in Rust, + takes only an owned String and a &str |
| ToPrimitive |
Debug and Display ↗ |
in Rust, two text forms, and only one is generated for you |
< and > |
Comparing and sorting text ↗ |
in Rust, strings compare as UTF-8 bytes, which is code-point order |
var, let and const |
Scope is about names, not values ↗ |
in Rust too, a name's scope ends at the closing brace |
var, let and const |
const and static ↗ |
in Rust, const is a value substituted at compile time |
const is not frozen |
Interior mutability ↗ |
a Rust binding without mut freezes the value, except through interior mutability |
| Hoisting and the temporal dead zone |
Uninitialized reads ↗ |
where JavaScript throws, C reads garbage and Rust refuses to compile |
| Closures |
What a closure is ↗ |
a Rust closure is a struct holding what it captured |
| Closures |
The move keyword ↗ |
in Rust you choose whether a closure borrows or moves |
| Closures in loops |
The move keyword ↗ |
a Rust closure made in a loop moves or borrows that turn's value |
| Destructuring |
Destructuring structs ↗ |
struct patterns in Rust name the fields you want |
| Destructuring |
Irrefutable patterns ↗ |
which patterns a Rust let accepts, and why |
| Destructuring |
let else ↗ |
a pattern that may fail, with an else to handle it |
| Declarations, expressions and arrows |
Functions ↗ |
how Rust writes a function: every type spelled out, the last expression returned |
this |
impl blocks ↗ |
how Rust avoids the question: self is a parameter the method declares |
| Arrow functions |
What a closure is ↗ |
how a Rust closure captures what it sees where it was written |
call, apply and bind |
impl blocks ↗ |
how Rust treats a method: an associated function whose first parameter is self |
| Parameters |
Optional function arguments ↗ |
how Rust copes with no default parameters at all |
| Functions are objects |
Function pointers ↗ |
how Rust holds a function: eight bytes of code address, no properties |
| Higher-order functions |
Iterators are lazy ↗ |
how Rust's map differs: a lazy adapter, where an array's map runs at once |
| Recursion and the call stack |
Recursion and the size of the stack ↗ |
how Rust ends an overflow: the process aborts, nothing to catch |
| Recursion and the call stack |
The call stack: what a call does to memory ↗ |
what one call does to stack memory, measured in Rust |
| Object literals |
A type is not a constructor ↗ |
how Rust's struct literal differs: it names a declared type |
| Property keys |
HashMap ↗ |
how a Rust HashMap orders keys: differently on every run |
| Getters and setters |
Naming conventions ↗ |
how Rust does without properties: a getter is a method, named without get_ |
| Copying objects |
Copy vs Clone ↗ |
how Rust makes copy versus clone a property of the type |
| Copying objects |
What a clone costs ↗ |
what a deep copy costs, counted in allocations, in Rust |
?. and ?? |
The ? operator ↗ |
how Rust's ? stops early: by returning from the whole function |
?. and ?? |
unwrap_or: the default you already have ↗ |
how Rust supplies a fallback for a missing value |
| JSON |
The round trip ↗ |
the round-trip test that catches what serialization drops |
| The prototype chain |
Method resolution ↗ |
how Rust's dot searches instead: at compile time, through derefs |
| Classes |
A type is not a constructor ↗ |
how Rust differs: a type is not callable, the class here is |
| Classes |
impl blocks ↗ |
how Rust keeps methods apart from data, in impl blocks |
new |
A type is not a constructor ↗ |
how Rust builds values without new: Type::new is an ordinary function |
| Private fields |
Modules and visibility ↗ |
how Rust draws the privacy wall: around a module, not a class |
| Static members and static blocks |
impl blocks ↗ |
how Rust spells a static method: an associated function called on the type |
extends and super |
Supertraits ↗ |
how Rust does without inheritance: a supertrait is a requirement, not a parent |
extends and super |
What a trait is ↗ |
how Rust shares behaviour between types with traits |
| Arrays |
Array or Vec? ↗ |
in Rust, fixed arrays and growable Vec are separate types; JavaScript has one |
sort() compares strings |
slice::sort_by ↗ |
in Rust, numbers sort numerically and a closure is needed only for other orders |
| Mutating and copying methods |
slice::sort ↗ |
in Rust, sort also works in place, and returns nothing |
map, filter, reduce and friends |
Iterators are lazy ↗ |
in Rust, map computes nothing until consumed; an array's map runs at once |
map, filter, reduce and friends |
fold and reduce ↗ |
in Rust, fold takes the initial value and reduce returns an Option |
Map and Set |
HashMap ↗ |
in Rust, HashMap iterates in a different order on every run |
Map and Set |
BTreeMap and BTreeSet ↗ |
BTreeMap iterates in key order, a third choice |
WeakMap and WeakSet |
Rc: the clone that copies a pointer ↗ |
in Rust, Weak also points at a value without keeping it alive |
| Grouping and set operations |
HashSet ↗ |
the four set operations of a Rust HashSet |
| Code points |
Walking a String ↗ |
in Rust, a string is walked by char, which is a code point |
| Template literals |
The format mini-language ↗ |
in Rust, formatting uses a mini-language instead of interpolated expressions |
| Comparing and sorting strings |
Comparing and sorting text ↗ |
in Rust, strings compare byte by byte and collation needs a crate |
| Every number is a double |
What a float actually stores ↗ |
the same 0.1, printed to thirty digits in Rust |
| Safe integers |
Big integers: BigInt and Python's int ↗ |
the u128 ceiling in Rust, and the Python int that has none |
| Parsing numbers |
Parsing out of a string ↗ |
in Rust, parse returns a Result where JavaScript returns NaN |
| Rounding and formatting |
Making a float whole ↗ |
floor, ceil, trunc and round in Rust, on negatives and halves |
| Bitwise operators |
Bit flags ↗ |
masks and shifts on fixed-width integers in Rust |
| Random numbers |
Randomness: std has none ↗ |
in Rust, the standard library ships no random generator at all |
| Money |
Decimals: rust_decimal, bigdecimal and Python's Decimal ↗ |
decimal types in Rust and Python, and integer cents |
| Money |
Scale the denominator away ↗ |
scaling the denominator away, then summing in plain integers |
if, switch and ? : |
match expressions ↗ |
the Rust match, which must cover every case and never falls through |
if, switch and ? : |
if expressions ↗ |
in Rust if is an expression, so there is no ? : |
| Loops |
Loop labels ↗ |
the same labelled break, spelled 'outer in Rust |
| Loops |
break ↗ |
in Rust break can carry a value out of a loop |
| Short-circuit evaluation |
Meet the bool ↗ |
in Rust && and || take and return only bool |
for...in and for...of |
for loops ↗ |
the Rust for, which has only the value-walking kind |
| The iteration protocol |
Implementing Iterator ↗ |
the Rust protocol: one next returning an Option |
| Iterator helpers |
Iterators are lazy ↗ |
the Rust iterator adapters, lazy in the same way |
| Iterator helpers |
Adapters by job ↗ |
the Rust adapters, grouped by what each is for |
| Async iteration |
Iterator versus Stream ↗ |
Stream, the async counterpart of Iterator in Rust |
throw and try...catch |
What makes a type an error ↗ |
what Rust requires of a type before calling it an error |
throw and try...catch |
The ? operator ↗ |
in Rust errors are returned as values, not thrown |
finally |
Drop, and what RAII buys ↗ |
cleanup by scope instead, with Rust's Drop |
Custom errors and cause |
anyhow and context ↗ |
adding context to an error in Rust while keeping the source |
Custom errors and cause |
thiserror vs anyhow ↗ |
the two ways Rust defines and wraps error types |
| Stack traces |
Reading a backtrace ↗ |
the Rust counterpart, printed when a program panics |
| Promises |
What a future is ↗ |
a Rust future does nothing until polled; a promise starts at once |
async and await |
async fn and .await ↗ |
the same keywords in Rust, where nothing runs until awaited |
| ES modules and CommonJS |
Packages and crates ↗ |
the unit Rust builds: a Cargo package and its crates, not a file |
| ES modules and CommonJS |
One module per file ↗ |
how Rust maps files to modules, with a single system |
import and export |
Bringing names in with use ↗ |
the Rust counterpart: use binds a name, checked at compile time |
import and export |
Modules and visibility ↗ |
the Rust default: nothing leaves a module unless marked pub |
Dynamic import() and top-level await |
Conditional compilation ↗ |
the compile-time alternative: Rust decides which code exists before it runs |
package.json |
Packages and crates ↗ |
what a Cargo package contains and what it exposes |
require and ES modules |
Two versions of one crate in one binary: what "SemVer compatible" means to Cargo ↗ |
the Cargo version of one library loaded twice in one program |
| Temporal |
An Instant is not a SystemTime ↗ |
the same split in Rust: an instant is not a calendar time |
| Measuring elapsed time |
Two clocks: Instant and SystemTime ↗ |
the same two kinds of clock in Rust, and which can be set |
| Well-known symbols |
Implementing Iterator ↗ |
the Rust counterpart of Symbol.iterator: the Iterator trait |
| Tagged templates |
Why println! is a macro ↗ |
the compile-time alternative: Rust checks its format string before running |
| Decorators |
What an attribute is ↗ |
attribute syntax in Rust, expanded at compile time |
using and Symbol.dispose |
Drop, and what RAII buys ↗ |
the Rust version: Drop runs at the end of scope, no keyword needed |
using and Symbol.dispose |
Drop guards ↗ |
a value whose only job is cleanup on every exit |
| Reachability |
Rc: the clone that copies a pointer ↗ |
counting owners instead of tracing from roots: Rust's Rc |
WeakRef and FinalizationRegistry |
Drop, and what RAII buys ↗ |
a destructor that runs at a known point, unlike a finalizer |
| Measuring memory |
The global allocator ↗ |
counting heap bytes in Rust with a replacement allocator |
| Measuring memory |
Stack and heap ↗ |
where Rust puts values, with no collector at all |
process |
Arguments and the environment ↗ |
arguments and the environment in Rust |
process |
Standard error, and exit status ↗ |
standard error and the exit status in Rust |
| Paths and file URLs |
Path and PathBuf ↗ |
paths as their own type, in Rust |
| stdin, stdout and pipes |
Broken pipe ↗ |
what a closed pipe does to a Rust program |
| Streams |
Backpressure ↗ |
backpressure in a Tokio server |
| HTTP |
An HTTP request ↗ |
the same request from Rust |
| HTTP |
Mocking a server ↗ |
a local server standing in for the real one |
| The built-in test runner |
How cargo test runs your tests ↗ |
how cargo test finds and runs tests |
| The built-in test runner |
cargo-nextest: one process per test, and what that actually buys ↗ |
one process per test, beside Node's one per file |
| One thread per agent |
Concurrency or parallelism ↗ |
the same distinction, drawn in Rust |
| Worker threads |
Spawning a thread ↗ |
spawning a thread in Rust |
postMessage |
Ownership and moves ↗ |
a move in Rust, checked at compile time instead |
| The DOM |
Rust in the browser ↗ |
how Rust reaches the DOM: only through JavaScript glue |
fetch and CORS |
An HTTP request ↗ |
an HTTP client outside the browser, where CORS never applies |
| Loading scripts |
Shipping a wasm page ↗ |
a module that also needs the right MIME type |
| What TypeScript adds |
What a compiler does before your program runs ↗ |
rustc also rejects code before it runs, and its types reach the machine code |
| Running TypeScript |
Compiled, interpreted, or something between ↗ |
when a toolchain compiles, and what runs afterwards |
tsc and tsconfig.json |
What cargo run -v shows: the rustc line, flag by flag ↗ |
a project file turned into compiler flags, printed by Cargo |
strict |
Strict clippy: denying the panic, and the arithmetic that comes with it ↗ |
the same trade in Rust: stricter settings, more errors, fewer crashes |
| Annotations and inference |
Type inference ↗ |
in Rust, inference also uses later lines; TypeScript uses the initialiser |
| Annotations and inference |
What a type annotation does ↗ |
an annotation as an input to inference, in Rust |
| TypeScript 7 |
rustup: the rustc you run is not the compiler ↗ |
another command on PATH that only stands in for the compiler |
| TypeScript 7 |
Pinning the toolchain: which compiler verified the answer keys? ↗ |
pinning the compiler that verified the answer keys, in Rust |
| Arrays and tuples |
Tuples ↗ |
a Rust tuple really does have a fixed length |
| Arrays and tuples |
Array or Vec? ↗ |
fixed-size array or growable Vec, the choice TypeScript blurs |
| Object types |
Option fields: modelling what may be absent ↗ |
how Rust marks a field that may be missing |
| Union and intersection types |
What a union is ↗ |
a Rust union is untagged memory, a different thing with the same name |
| Union and intersection types |
Variants that carry data ↗ |
a Rust enum adds possibilities the way a union type does |
| Type aliases and interfaces |
A type alias is not a new type ↗ |
in Rust too, type gives a second name, not a new type |
| Type aliases and interfaces |
What a trait is ↗ |
a Rust trait names behaviour where an interface names a shape |
| Function types |
Optional function arguments ↗ |
no optional parameters and no overloading in Rust, and what replaces them |
strictNullChecks |
Partial functions: why Option exists ↗ |
why Rust's find returns an Option instead |
strictNullChecks |
Null dereference ↗ |
the same crash in C, and Rust's separate type for absence |
any, unknown and never |
The never type ! ↗ |
the Rust type of an expression that never finishes |
| Enums |
What an enum is ↗ |
a Rust enum is a closed type that match must cover |
| Enums |
An enum instead of a bool ↗ |
named states instead of a bare flag, in Rust |
| Narrowing |
if let: one arm, and move on ↗ |
narrowing by pattern instead of by test, with Rust's if let |
| Truthiness narrowing |
Meet the bool ↗ |
no truthiness in Rust: if count does not compile |
| Truthiness narrowing |
Six kinds of zero ↗ |
keeping zero and missing apart as different values, in Rust |
| Discriminated unions |
Variants that carry data ↗ |
the same idea built into Rust as enum variants |
| Discriminated unions |
Destructuring enums ↗ |
taking a tagged value apart with a pattern, in Rust |
| Exhaustiveness checking |
match expressions ↗ |
a match in Rust must cover every case with no helper |
| Exhaustiveness checking |
The wildcard _ ↗ |
the catch-all arm that switches Rust's check off |
| Type predicates |
expect: writing down the proof ↗ |
a claim the compiler cannot check, written down, in Rust |
satisfies, as and annotations |
Casting with as ↗ |
an as in Rust converts the value; TypeScript's converts nothing |
| Structural typing |
What a trait is ↗ |
traits in Rust are nominal: a type fits only after an impl |
| Excess property checks |
When a struct refuses ↗ |
a Rust struct literal refuses an unknown field every time |
| Branded types |
A score is not a number: the newtype ↗ |
the Rust newtype, which gives the same safety as a real type |
| Branded types |
Phantom types ↗ |
a zero-byte type tag in Rust, the closest match to a brand |
| Variance |
When the type checker is wrong ↗ |
what soundness means, from a checker that promises it |
| Generic functions |
What a generic is ↗ |
each Rust instantiation gets its own copy; TypeScript erases T entirely |
| Constraints |
Where the bound goes ↗ |
a trait bound, Rust's constraint, and where it belongs |
| Generic types |
Generic enums ↗ |
generic enums in Rust, where Option is ordinary library code |
const type parameters |
Const generics ↗ |
a different idea with a similar name: Rust's const generic takes a value |
| When inference guesses wrong |
When the compiler cannot infer ↗ |
rustc's E0282: the same gap, reported as an error instead of unknown |
| Recursive types |
A generic recursive type ↗ |
in Rust a Box must break the cycle; a type alias needs nothing |
private versus #private |
Modules and visibility ↗ |
privacy in Rust, also compile-time, but drawn around modules |
abstract and implements |
What a trait is ↗ |
a Rust type meets a trait only through an impl; implements is optional |
readonly and override |
Borrowing: &T, &mut T, and where a borrow ends ↗ |
a Rust shared borrow forbids writes through every path, not one name |
| Declaration files |
Generating bindings ↗ |
declarations of C code for Rust, generated because hand-written ones drift |
| Declaration merging |
Extension traits ↗ |
the Rust way to add a method to a foreign type, with code behind it |
any is contagious |
What unsafe turns off ↗ |
the Rust contrast: unsafe lifts five checks in one block; any spreads |
| Type assertions |
Casting with as ↗ |
the Rust as converts the value; TypeScript's as converts only the type |
| Type assertions |
mem::transmute ↗ |
the nearest Rust match to as unknown as T, checking only size |
| Index access |
Partial functions: why Option exists ↗ |
in Rust get returns Option, so the missing element is in the type |
| External data |
Deriving Serialize and Deserialize ↗ |
serde checks the shape while it parses; JSON.parse checks nothing |
| Run-time validation |
Validating at the boundary ↗ |
the same rule at Rust's C boundary: validate first, then trust |
| Array covariance |
When the type checker is wrong ↗ |
soundness defined: in rustc a hole is a bug, here it is a choice |
| npm |
Adding a dependency: search, info, add ↗ |
the caret in Cargo, which follows the same rule below 1.0 |
| npm |
Cargo.lock: what it records, who reads it, and the command that ignores it ↗ |
the lockfile in Cargo, and the command that ignores it |
npm run and npx |
Cargo subcommands worth knowing ↗ |
subcommands in Cargo, the nearest thing to npm scripts |
| Editor setup |
Zed for Rust: one key to save and run, and three defaults in its way ↗ |
the same editor, set up for Rust |
| Editor setup |
Choosing an editor for Rust ↗ |
choosing an editor for Rust |
| ESLint |
Strict clippy: denying the panic, and the arithmetic that comes with it ↗ |
clippy, Rust's linter, set to deny |
| Prettier |
Formatting: rustfmt, and the formatter your IDE swaps in behind it ↗ |
rustfmt, and the formatter an IDE swaps in |
| Bundlers and transpilers |
A build system is not a compiler ↗ |
a build system is not a compiler |
| Bundlers and transpilers |
What a compiler does before your program runs ↗ |
what a compiler does before your program runs |