> [!quote] Rob Pike on Go > The key point here is our programmers are [[Software Engineering at Google|Googlers]], they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt. People were initially confused by Go's positioning, as it originally described itself as a *systems programming language*, placing it against [[C|C++]]. Programmers wondered how a managed language can compete with C++. The answer is you can't; [[Rust]] challenged C++ where C++ was best at. However, what Rob Pike had in mind as a systems language is a language that can be used to write core, low-level services in a tech company. From this angle, it was tremendously successful and took over [[Java]] and [[Python]]. At this point (2024), I really like Go, especially after generics were added. # Per-version evolution > [!note] See [[Go - Versions]] for major per-version improvements # Style Guides - [Uber's Style Guide](https://github.com/uber-go/guide/blob/master/style.md) - [Copy Slices and Maps at Boundaries](https://github.com/uber-go/guide/blob/master/style.md) is an interesting one. Mutability in a large software without any mutability control (`final`, `const`, or `Immutable*` containers) is difficult. - [Google's Style Guide](https://google.github.io/styleguide/go/) - [[Go - The Loop Variable Bug]]. Many style guides warned against this, as this was a common gotcha. It is no longer needed. # Language Details ## Secret Sauce Even though Go is regularly mocked as a brain-dead programming language, it still has features that are relatively novel: * [[Green Threads]] * [[Channels and Goroutines]] and support for the true N:M multithreading model. * [Go Scheduler](https://nghiant3223.github.io/2025/04/15/go-scheduler.html)'s GMP (Goroutine-Thread-Processor) model. * Structural Subtyping * not sure why this isn't endorsed as much; even in the camp of statically typed languages, [[C++]] is the original "duck typed" language (templates, before `concept`s). [[TypeScript]] is fundamentally structurally typed (in fact, it's almost impossible to get out of it). [[Typed Python]] now has `Protocol` * [[Record vs Classes|Value Types]] * [[Go - Generics|Reified Generics]] (1.18+) * Overall [[#Developer Experience]] * [[Defer]] > [!thought] > We thought Value Types were unnecessary under the [[Sufficiently Smart Compiler or VM|Sufficiently Smart VM]]. Escape analysis allow variables to be stack allocated. > > However, knowing that a variable WILL be stack allocated (and will have pass-by-value semantic) is very powerful. Java double-suffered this due to its erasure-based generics. `ArrayList<T>` requires at least `N+1` allocations and multiple pointer dereferences, making Java very difficult for high-performance computing. Note that some of these features are now part of [[Java]] (ex: Virtual Threads, Value Types). ## High performance [[Asynchronous Programming]] without thinking about it Go is built with goroutines from day 1, and every underlying standard library call (IO and syscalls) work natively. It is one language where you have access to high-performance async IO by default. In other languages ([[Python]] and [[Java]]), most APIs still assume synchronous APIs, and you're still forced to mix synchronous and asynchronous code. [[Rust]] has the same problem of the bifurcated ecosystem. ## Developer Experience Usability-wise, this is the area that Go pioneered from the day 1. * **Compilation time** 🔥🔥🔥🔥🔥🔥🔥. This is one I miss the most whenever I use another language. * the initial language is optimized for fast compilation time. * packages as compilation units. * no type inferencing that requires multiple pass. * no generics to bloat code generation. * however, the current bottleneck to go compilation is linker. * this is problematic for go's testing framework as it links an executable per package. * **Single Static Binary** * Though, *linking* is notoriously slow for a decently sized go repositories. If you pull in a large dependency like [[Kubernetes]], then your binary will be 100+MB with 10s of seconds of linking. * `go fmt` and elimination of customization. Many other formatters (`black`, `prettier` took this authoritarian approach). #### Lack of (initial) features Go surprisingly got away with shipping a relatively bare-bone language. [[Go - Versions]] has list of features added over time. Generics is the most glaring omission. Is Go going against the years of PL research? How can one ship a statically typed language without generics in the 2000s? It was surprisingly workable. - with *interfaces*, even though some abstractions (sorting) had a weird signature. - Built-in types (arrays, slices, maps, and channels) were parameterized and it provided the escape hatch for significant need for generics. Given Go's caution around complexity and compilation time. It was the right move (Languages like [[Rust]] and [[Scala]] are notorious for their slow compilation time). Astonishingly, Go added generics to the core language without creating a big chasm in the ecosystem. Another missing feature was [[Package Manager]]. This made sense for [[Google]]; they develop in a single [[Monorepo]], where all dependencies are first party. Go's decision to piggy back off github, where: - `go get` naturally works against git repositories - git URLs form the package name This worked surprisingly well too. However, this was hopelessly naive and builds can never be isolated nor hermetic. The history is circuitous, evolving between: - manual vendoring - go's "official" support for vendoring. - vendoring tools - `gomod` and `glide` - `dep` (go's official experiment) - ...and finally, go modules. ## [[Convention over Configuration]] - Using capitalization to control a symbol's access level - `privateSymbol` vs `PublicSymbol`. - `internal` directory - `vendor` directory - [[Go Pragmas]] ## Strict Type System very little type inferencing / automatic casting. - Various `int` and `uint` types must be explicitly cast between operations and assignments. So are between different-sized `int` and `uint` types. - no [[Covariance and Contravariance]] when it comes to interface satisfaction. - no [[Function Overloading]] > [!warning] Few exceptions: > **`const` assignments** - `const`s are different in Go than other languages; they are not constant variables (C style) but untyped (mostly) constant literals. > ```go > const c = 3 > var x int = c > ``` > **interface assignment:** > ```go > var x ConcreteType > var y Interface = x; // does not require a type cast. > ``` > **Channel Conversion:** > ```go > var x chan T > var read <-chan T = x // does not require a type cast. > var write chan<- T = x > ``` ## [[Go - context.Context|context.Context]] ## [[Slices]] ## [[Go Enums]] ## [[Go Iterators]] ## [[Go Modules]] ## [[Go Interface]] ## Struct Tags Go's answer to the [[Annotation-based meta-programming]]. In the struct declaration, addition field-level metadata can be specified via the backticks: ```go type Point struct { X int `json:"x"` Y int `json:"y"` } ``` However, struct tags form key-value pairs of the form `key:"value" key:"value" ...`: > [!quote] [link](https://pkg.go.dev/reflect#StructTag.Get) > By convention, tag strings are a concatenation of optionally space-separated key:"value" pairs. Each key is a non-empty string consisting of non-control characters other than space (U+0020 ' '), quote (U+0022 '"'), and colon (U+003A ':'). Each value is quoted using U+0022 '"' characters and Go string literal syntax. It's an interesting tradeoff as this is not strictly typed like Java, nor allows for resolving namespace conflicts ([well-known struct tags](https://go.dev/wiki/Well-known-struct-tags) tracks them). The `json` struct tag is shared everyone, whether you like it or not. In practice, this doesn't cause that much problem. Single-level struct tags are sufficient enough for most use cases, and namespace conflict seldom arise (if a type is involved in multiple situation that requires conflicting struct tags, multiple types can be used instead). [[Go Complaints]] # Design Documents - [Building a better Go linker](https://docs.google.com/document/d/1D13QhciikbdLtaI67U6Ble5d_1nsI4befEd6_k1z91U/view) # Other ## [[Go Templates]] Go templates are perhaps most unintuitive thing about go; Other than the fact that this is built into the language, I see no reason to use it. However, because of the popularity of the [[Kubernetes]] and its ecosystem heavily using the go templates, it became one template language that everyones' forced to use.