PONY λ M2 Modula-2
for Rust programmers

You already know Rust.Now explore other languages.

Side-by-side, interactive cheatsheets for Rust programmers
comparing Rust to other languages. Every example runs live in your browser — no setup, no installation.

▶ Start with Go Browse comparisons ↓

Choose your own path by reordering languages

Go Pre-Alpha

Go's systems niche, with a garbage collector. A Rust developer reaches for Go when they want fast compiled binaries and goroutines without a borrow checker, lifetimes, or unsafe — trading ownership and zero-cost abstractions for GC simplicity and shorter compile times.

  • Garbage collection instead of ownership — no borrow checker, no lifetimes, no Arc<Mutex<T>> boilerplate; the GC handles memory automatically
  • Goroutines and channels — built-in structured concurrency without async/await or a separate runtime; go func() launches a lightweight coroutine instantly
  • Errors are plain return values — (value, error) tuples and if err != nil instead of Result<T, E> and the ? operator
  • Interfaces are satisfied implicitly — no impl Trait for Type declaration; if the methods match, the type qualifies
  • Sub-second incremental builds even for large projects — where Rust's borrow checker and monomorphization can take minutes, Go compiles a full binary in seconds
  • nil instead of Option<T> — simpler to write, but the compiler no longer forces you to handle the absent case
C Pre-Alpha

The language Rust was designed to replace. Drop the borrow checker, ownership, and Result: C gives you raw pointers, malloc/free, and a preprocessor — the model that Rust's safety guarantees exist to tame.

  • All storage is mutable by default — no let/let mut distinction; immutability requires an explicit const qualifier
  • No ownership system — malloc allocates and free releases; the compiler never checks: leaks, double-frees, and use-after-free compile silently
  • Pointers everywhere, no lifetime annotations — int *pointer is like a raw *mut i32 with no borrow checker to protect you
  • Strings are null-terminated char arrays — use strcmp instead of ==, snprintf instead of format!, and manage buffer sizes by hand
  • No generics — void* and casting, or preprocessor macros, replace Rust's parameterized types
  • switch falls through between cases unless you break — the opposite of Rust's exhaustive, non-fallthrough match
Swift Pre-Alpha

The Apple-platform sibling with a gentler memory model. Swift keeps Rust's value-type structs, algebraic enums with associated values, and protocols that mirror traits — but replaces the borrow checker with ARC, adds first-class optionals with ? syntax, and brings argument labels that make call sites read like prose.

  • let is immutable in both, but Swift mutates with var instead of let mut
  • ARC (automatic reference counting) replaces the borrow checker — no ownership rules, no lifetime annotations, but retain cycles are possible
  • Optionals use T? syntax and integrate with optional chaining (value?.property) — more concise than Option<T> and .map()
  • Enums with associated values work nearly identically — exhaustive switch matches just like Rust's match
  • Swift argument labels (func move(to point: CGPoint)) make call sites read like natural language — Rust has no equivalent
  • Actors provide built-in data-race safety for async code — a language-level alternative to Arc<Mutex<T>>
TypeScript Alpha ⚡ Works Offline ⚡ Offline

TypeScript trades Rust's ownership model and native binary output for a gentler learning curve, a garbage-collected runtime, and the entire npm ecosystem. A Rust developer moving to TypeScript swaps Result for exceptions, Option for null/undefined, and the borrow checker for structural typing on a single-threaded event loop.

  • Structural typing — any object with the right shape satisfies an interface; no impl Trait for Type declaration needed
  • Union types and discriminated unions (type Shape = Circle | Rectangle) replace Rust's algebraic enum variants without exhaustiveness enforcement by default
  • Exceptions instead of Result<T, E>throw/try/catch propagate errors invisibly through the call stack with no ? operator
  • Garbage collection — no ownership, no lifetimes, no borrow checker; the runtime handles memory automatically
  • The entire npm ecosystem — millions of packages, instant npm install, no Cargo.toml
  • Single-threaded event loop with async/await instead of multi-threaded futures — safe from data races by design, but limited to one CPU core per process
Zig Pre-Alpha

Optimal software with no hidden control flow. Like C but with memory safety, error handling, and compile-time code execution built in.

  • Allocators instead of a garbage collector — every heap allocation is explicit and the allocator can be swapped without changing library code
  • Error unions (!T) and comptime-checked exhaustive switch — impossible to ignore an error or miss a case the way Ruby's rescue and case/when allow
  • Comptime replaces macros, generics, and metaprogramming with a single mechanism: Zig code that runs at compile time
  • No hidden control flow — no operator overloading, no implicit conversions, no exceptions unwinding through stack frames
  • Optional types (?T) enforced at compile time — the null pointer problem that Ruby solves with nil checks is solved in Zig before the program even runs
Haskell Pre-Alpha

Correctness at compile time, from a completely different angle. Rust proves safety through ownership and the borrow checker; Haskell proves it through purity, laziness, and a typeclass system so principled that Rust's own traits were partly inspired by it.

  • No ownership model at all — Haskell is garbage collected, so there are no lifetimes, no borrow checker, and no move semantics to fight
  • Maybe and Either mirror Rust's Option and Result almost one-to-one — the safe-navigation idioms you already use transfer directly
  • Typeclasses parallel Rust's traits closely, right down to default method implementations and constraint syntax
  • Lazy evaluation by default, versus Rust's eager evaluation — infinite lists and on-demand computation fall out naturally instead of requiring a special iterator type
  • Every function is curried automatically — partial application needs no closure boilerplate the way Rust's does
  • The ? operator on Result is Rust's closest brush with monadic chaining — Haskell's do-notation and >>= generalize that exact pattern to any type with a Monad instance
C++ Pre-Alpha

The language Rust was designed to replace. C++ shares Rust's zero-overhead philosophy and deterministic memory management — but without the borrow checker, relying on conventions and RAII discipline instead of compile-time enforcement.

  • RAII and destructors — C++'s resource cleanup is the same idea as Rust's Drop, implemented via class destructors that run at end of scope
  • Smart pointers — unique_ptr is Box, shared_ptr is Arc, weak_ptr is Weak; same ownership model, no compiler enforcement
  • Templates vs generics — C++ templates are more powerful but unconstrained by default; C++20 Concepts are the equivalent of Rust trait bounds
  • std::variant + std::visit vs Rust enums — the same tagged-union idea, but three times more verbose without pattern matching
  • std::expected<T,E> (C++23) mirrors Result<T,E> — error-as-value without exceptions; C++ still has no ? operator
  • C++20 ranges and std::views pipeline syntax closely resembles Rust's iterator chains, but materializing to a collection requires extra boilerplate
Roc Pre-Alpha

The spirit of Rust with the execution model inverted. Roc's original compiler was written in Rust, Roc programs are typically hosted by Rust or Zig platforms, and both languages bet everything on compile-time correctness without a garbage collector — but where Rust puts you in charge of ownership and lifetimes, Roc handles memory with compile-time reference counting and zero annotations.

  • No borrow checker, no lifetimes, no move — automatic compile-time reference counting gives the same "no GC pauses, no manual frees" guarantee with none of the bookkeeping
  • Try(ok, err) is Roc's ResultOk/Err, pattern matching, and ?-style propagation all transfer directly from Rust
  • Tag unions are Rust enums without the ceremony — anonymous, structural, and inferred, with the same exhaustive match checking
  • Effects are visible in every signature — the ! suffix marks effectful functions the way unsafe marks unsafe blocks: you always know what a call can do
  • The platform model inverts Rust's world — instead of your program calling a library, a host program (often written in Rust) embeds and calls your Roc code, providing all I/O
  • Everything is an expression and everything is immutable — no mut, no statements-vs-expressions divide, no semicolons
Ruby ⚡ Works Offline ⚡ Offline

What Rust developers reach for when the borrow checker isn't the point. Ruby trades ownership, Result, and compile-time guarantees for a garbage-collected world where everything is mutable by default, every value is an object, and blocks let callers control what any method does.

  • No ownership, no lifetimes — the garbage collector manages memory; no Arc<Mutex<T>>, no move, no borrow errors to reason about
  • Exceptions instead of Result — the happy path is never interrupted by match or ?; errors propagate automatically until something rescues them
  • Blocks and yield — every method can receive an anonymous block of code; array.map { |x| x * 2 } with no Fn trait bound to declare
  • Dynamic typing — no type annotations; a variable holds any object and can change type on reassignment, with no let keyword needed
  • Open classes — reopen String, Integer, or any class at runtime and add methods, without the orphan rule blocking you
  • Mutable by default — there is no let mut; every variable is freely reassignable and mutation is the norm rather than an opt-in
Drag cards to reorder · your order is saved locally