A small, heavily-commented Go project built for teaching. Every file is written to be read top-to-bottom: the comments explain not just what the code does but how Go works and why it's designed that way.
It also shows how Go expresses object-oriented ideas despite having no classes or inheritance.
# Run the entire tour:
go run .
# Or run a single lesson:
go run . basics
go run . oop
go run . concurrencyDon't have Go? Install it from https://go.dev/dl/ (or
brew install goon macOS). No third-party dependencies are needed — only the standard library.
A Makefile wraps the common workflows in short commands (a de-facto
convention in Go projects). Run make help to see them all:
make help # list every target
make run # go run . (also: make run-basics / run-oop / run-concurrency)
make test # run all tests (make test-verbose for per-test output)
make cover # tests + coverage summary
make bench # run benchmarks
make race # run tests under the data-race detector
make check # fmt + vet + test — run this before committing
make build # compile to ./bin/gosample
make clean # remove build artifactsgo-sample/
├── go.mod # module definition (import path + Go version)
├── Makefile # short commands for build / test / lint workflows
├── main.go # entry point; dispatches to each lesson
│
├── basics/ # core language fundamentals
│ ├── basics.go # lesson entry point (Run)
│ ├── variables.go # vars, constants, types, zero values, pointers
│ ├── controlflow.go # if / for / switch / defer
│ ├── functions.go # multiple returns, variadic, closures
│ ├── collections.go # arrays, slices, maps
│ ├── errors.go # errors as values, panic/recover
│ ├── generics.go # type parameters, constraints, generic types
│ ├── json.go # encoding/json + struct tags
│ └── basics_test.go # tests for the generic helpers and JSON
│
├── oop/ # object-oriented patterns in Go
│ ├── oop.go # lesson entry point + mental model
│ ├── structs.go # structs, methods, value vs pointer receivers
│ ├── interfaces.go # implicit interfaces, polymorphism, type switch
│ ├── embedding.go # composition that mimics inheritance
│ ├── encapsulation.go # exported vs unexported (package-level privacy)
│ └── oop_test.go # unit tests: table-driven, subtests, example, benchmark
│
└── concurrency/ # Go's signature feature
└── concurrency.go # goroutines, channels (buffered, close/range),
# select, worker pool, context, WaitGroup, Mutex
- Static typing with inference —
var,:=, and explicit conversions. - Zero values — every type has a defined default; no uninitialized memory.
- One loop keyword —
fordoes the job ofwhileanddo/whiletoo. - No-fallthrough
switch— the opposite default from C/Java. - Multiple return values & closures — the building blocks of Go functions.
- Slices & maps — the everyday collections, and the gotcha that slices share their backing array.
- Errors are values — handled with
if err != nil, not exceptions. - Generics — type parameters, custom constraints (
~int | ~float64), thecmp.Orderedstandard constraint, and generic types (Stack[T]). - JSON & struct tags —
encoding/jsonmarshal/unmarshal, key renaming,omitempty, and skipping fields with-.
Go is not class-based. It has no classes, constructors, or inheritance. Instead it offers a smaller, composable toolkit:
| OOP concept | Go mechanism |
|---|---|
| Object (data) | struct |
| Method / behavior | method with a receiver (func (a *Account) ...) |
| Constructor | convention: a New... function |
| Polymorphism | interface, satisfied implicitly (duck typing) |
| Inheritance/reuse | embedding (composition — "has-a", not "is-a") |
| Encapsulation | exported vs unexported names, enforced per package |
The big idea: composition over inheritance, and depending on what a type does (interfaces) rather than what it is (base classes).
- goroutines — launch concurrent work with the
gokeyword. - channels — pass data between goroutines with built-in synchronization, including buffered channels and closing + ranging over a channel.
select— wait on several channels at once.- worker pool — the classic pattern: N workers draining a shared jobs channel.
context.Context— cancellation, deadlines, and timeouts.sync.WaitGroup/sync.Mutex— wait for batches, and the lock-based alternative to channels.
"Don't communicate by sharing memory; share memory by communicating."
Go ships testing in the standard library — no external framework needed.
go test ./... # run all tests
go test ./oop/ -v # this package, verbose (shows each subtest)
go test ./oop/ -run Withdraw # only tests whose name matches "Withdraw"
go test ./oop/ -bench . # also run benchmarksThe test file demonstrates the core techniques:
TestXxx(t *testing.T)— basic tests;t.Errorf(continue) vst.Fatalf(stop).- Table-driven tests — list cases as data, loop over them (the idiomatic Go style).
- Subtests —
t.Run(name, ...)isolates and names each case. - Example functions —
ExampleAccount_Balanceis documentation and a test: its// Output:comment is checked against real output. - Benchmarks —
BenchmarkXxx(b *testing.B)measures performance. - White-box testing —
package oopcan reach unexported fields (e.g.value); usingpackage oop_testinstead would test only the exported API.
main.go— see how packages are imported and wired together.basics/files in the order listed above.oop/— readoop.gofirst for the mental model, thenoop_test.go.concurrency/concurrency.go.