Side-by-side, interactive cheatsheets for Go programmers
comparing Go to other languages. Every example runs live in your browser — no setup, no installation.
Choose your own path by reordering languages
Where Go programmers go when they want expressiveness over explicitness. Ruby trades Go's ceremony — error checks after every call, explicit loops, no generics-free verbosity — for blocks, exceptions, and an object model so consistent that even integers have methods.
map, select, reduce built into every collection, replacing Go's hand-written for-range loopsif err != nil; errors propagate until something rescues themString or Integer and add methods at runtime; define methods on the flycase/in destructures arrays and hashes and binds variables in one step, with no manual index checksTen thousand goroutines become one event loop. There is no go statement, no channel, no mutex and no -race — one thread, where blocking it stops everything. In exchange the data race cannot happen. Then: exceptions instead of if err != nil, two empties instead of nil, one f64 instead of the sized-integer family, and objects that never copy.
Promise.all is the shape a WaitGroup becomes, and only I/O actually overlapsselect becomes Promise.race without cancellation, and a worker pool is fifteen lines you writetotal++ cannot interleave; the races that remain are logical, across await pointsint64 arrives as a rounded double through JSON.parse, so send 64-bit identifiers as stringsGOOS=js GOARCH=wasm gets a section of its own, including why esbuild ships as a binary insteadWhere Go programmers go for data, scripting, and getting an idea running in five lines. Python trades Go's static types and compile step for dynamic typing, exceptions, comprehensions, and a vast library ecosystem — the lingua franca of data science and machine learning.
[x*x for x in items if ...] replaces the hand-written map/filter loops Go makes you spell out(value, error) and the endless if err != nil__add__, __repr__) — real OOP and operator overloading, which Go has none ofThe language Go was built by C people to replace, and still has to talk to. Most of what Go gives you is a small struct plus a discipline, and C makes you supply both by hand — which is exactly what reading a header for cgo requires.
{ pointer, length, capacity } — writing that struct out explains len, cap, why append must be reassigned, and why two slices can share one arrayC.CString exists to bridge, allocating a copy you must C.freedefer is the goto cleanup idiom, in reverse order, on every exit path, maintained for youFrom structs and goroutines to classes and Futures. Dart brings full OOP, sound null safety, named parameters, and async-first design to a Go programmer who already thinks in types.
String is non-nullable at compile time; String? makes it nullable. No nil panics at runtimerequired — replaces the fragile positional argument lists that Go forces on every API(value, error) returns — Dart propagates errors automatically; no if err != nil at every call siteGo's systems niche without the garbage collector — and with a type system that catches far more at compile time. Rust trades goroutines and GC for ownership, sum-type enums, and Result/Option, eliminating nil-panics and data races before the program ever runs.
iota constants or tagged structs, with exhaustive match the compiler enforcesResult and Option replace (value, error) and nil — the ? operator collapses if err != nil, and there is no null to dereferencemap/filter/fold as zero-cost abstractions, replacing Go's hand-written loopsThe same structural typing you already trust, with the type-level expressiveness Go leaves on the table. TypeScript interfaces are satisfied by shape — exactly like Go's — but the type system adds unions, literal types, and discriminated unions for the sum types Go never grew.
interface by its shape, not by declaring implementsnumber | string) and literal types ("active" | "inactive") express what Go can only fake with any and a type switchtry/catch, not returned as (value, error) — no if err != nil after every callThe systems language for Go programmers who want to drop the garbage collector. Zig keeps Go's small, readable spirit but makes memory explicit, errors part of the type system, and code generation happen at compile time — no runtime, no GC pauses, no hidden allocations.
defer/errdefer and no GC pausestype parameter!T) with try/catch replace (value, error) and the endless if err != nil?T) instead of nil — absence is in the type, and there is no null pointer to dereferenceswitch give the sum types Go lacks; no hidden allocations or control flow anywhereThe class-based, exception-driven system Go deliberately avoided. Java trades Go's implicit interfaces for an explicit implements keyword, its (value, error) returns for a full exception hierarchy, and its composition-only model for real inheritance — while sharing garbage collection and, since virtual threads, a genuinely lightweight concurrency story.
implements declarations instead of Go's implicit, structural interfaces — the single biggest philosophical gap between the two languagestry/catch/throws, checked vs. unchecked) instead of Go's explicit (value, error) return convention checked at every call siteextends inheritance instead of Go's struct embedding — a Java subclass genuinely is-a its parent, where Go composition only borrows fields and methodsThread used to be comparatively expensiveGo's readability without the garbage collector. Odin keeps defer, multiple return values, and zero-value initialization, then hands you the allocator — and adds the tagged unions and parametric procedures Go still makes you work around.
context carries the allocator — unlike Go's context.Context you never thread it through signatures, and reassigning context.allocator redirects every allocation below that pointunion types with an exhaustive switch give the sum types Go lacks — no more interface{} plus a type switch that silently misses a case$T is resolved at compile time and can take values, not just types: proc(array: [$N]$T) matches on the array lengthor_return collapses the if err != nil { return err } block into a single suffix, and or_else supplies a fallback inlinecore:thread and core:sync give you OS threads and explicit synchronization, with no scheduler in the binary#soa converts an array of structs into a struct of arrays while keeping items[index].field syntax — a data-layout change Go cannot express