Why does Go omit operator overloading, and what does that cost numeric code?
answer
- an operator has exactly one meaning
- reading + tells you it is cheap
- math/big spells addition as a method
- == belongs to the language, not the type
- time.Time documents Equal for a reason
basics
~20 sOperators in Go are fixed by the language: + and < work only on built-in types, and == is a compiler-defined field-wise comparison. Custom types express arithmetic and equality as ordinary methods, which makes numeric code verbose.
solid answer
~50 sGo gives operators exactly one meaning each and does not let a package add another. The payoff is at the reading end: when you see `a + b` you know it is a machine instruction on a built-in type — no allocation, no user code, no panic, nothing to look up. The cost lands on numeric libraries. `math/big` cannot write `x + y`, so it exposes `z.Add(x, y)` with an explicit destination, and every non-trivial expression becomes a chain of statements. Equality is the subtler cost: `==` on a struct compares every field, including unexported ones, which is why `time.Time` documents that you must call `Equal` instead — two values for the same instant compare unequal if one carries a monotonic clock reading. The language cannot force callers to use the method, so the type's real equality lives in documentation.
code
go · 6 linesx := big.NewInt(1 << 62)
y := big.NewInt(7)
// x + y does not compile; the sum is a method call
z := new(big.Int).Add(x, y)
z.Mul(z, big.NewInt(2))go deeper
Know the fact and one consequence: Go operators cannot be redefined, so packages like math/big do arithmetic through methods such as Add and Mul rather than through + and *.
Explain the trade in both directions — a fixed + guarantees a cheap machine operation with no hidden call, while numeric and money code becomes a chain of method calls. Mention that == is a compiler-defined field-wise comparison.
Bring the equality traps: == on time.Time compares the monotonic reading, a struct with a slice field will not compile under ==, and an interface holding an uncomparable dynamic type panics at run time. That is where the omission causes real bugs.
Own the framing when someone argues the language is unsuitable: overloading optimises the author of one numeric library, Go optimises the reader of everything else. Be able to say honestly when a workload sits on the wrong side of that trade.
## What Go fixes In Go, the meaning of every operator is defined by the language specification and cannot be extended by a package. `+` is addition on numeric types and concatenation on strings. `<` is defined on the ordered built-in types. `==` is defined structurally, by the compiler, on comparable types. No declaration anywhere can add a meaning for `+` on a user-defined type, and there is no `operator` keyword, no `__add__`, no `Symbol.iterator`-style hook. A named type built on a numeric type does keep the operators of its underlying type — `type Celsius float64` supports `+` — but that is inheritance of the built-in meaning, not a definition you wrote, and it will not let you add a `Celsius` to a `Fahrenheit`. ## The rationale The argument is the one behind most of Go's omissions: an operator is a very short name with very high visual weight, so redefining it moves a large amount of behaviour behind a symbol the reader cannot grep for. In a language with overloading, `a + b` may allocate, may take a lock, may perform I/O, may panic, or may be O(n). In Go it is a machine operation, and that certainty is worth something on every line of every file — particularly during review and when reading a profile, where an innocuous-looking expression can never be the hot spot in disguise. There is a second, quieter reason: overloading interacts badly with implicit conversion, and Go has no implicit numeric conversion either. Together those two absences mean an arithmetic expression has exactly one possible interpretation, decided locally, with no overload resolution and no promotion table. ## What it costs ### Arbitrary-precision and matrix code `math/big` is the standard example. Because `+` is unavailable, the package uses the receiver as an explicit destination: ```go z := new(big.Int).Add(x, y) z.Mul(z, big.NewInt(2)) ``` The convention (`z.Op(x, y)` returning `z`) is consistent and allows the caller to control allocation — a real benefit for tight loops — but a formula that reads as one line of algebra becomes several statements, and transcription errors get easy. The same shape recurs in any vector, matrix, decimal or money type. ### Equality `==` on a struct compares all fields, one by one, including unexported ones. That is often right and occasionally wrong, and the type author cannot correct it. `time.Time` is the canonical case: it carries a wall-clock reading, an optional monotonic reading, and a `*Location`, so two `Time` values denoting the same instant can be unequal under `==`. The package documents that you should use `t.Equal(u)`, and nothing in the compiler enforces it. Two further edges are worth knowing. A struct containing a slice, map or function field is **not comparable at all**, and `==` on it is a compile-time error. And comparing two interface values whose dynamic type is uncomparable **panics at run time** — the check moves from compile time to run time as soon as the value is boxed in an interface. ### Ordering `<` is likewise unavailable for user types, so sorting is expressed as a function: a `less` callback, a `Cmp` method returning -1/0/+1, or the `cmp.Ordered` constraint on a type parameter. Note the constraint only admits the built-in ordered types; it does not let you define `<` for your struct. ## The conventions that stand in Because the compiler will not help, the standard library's naming conventions carry the load, and you should follow them in your own types: `Add`, `Sub`, `Mul`, `Div` for arithmetic with an explicit destination or a value return; `Cmp` returning a three-way integer; `Equal` for semantic equality; `String` for display. A reviewer reading `a.Equal(b)` knows what it means because the whole ecosystem spells it the same way. ## How to answer the objection An engineer arriving from C++ or Python will say, correctly, that money and matrix code is uglier in Go. The right response is not to deny it. It is to note which side of the trade each language took: overloading optimises the writer of one numeric library, and Go optimises the reader of the ten thousand files that are not numeric libraries. If a codebase is mostly linear algebra, Go is a poor fit and that is a legitimate thing to say out loud.
- If == is fixed by the compiler, how does a type express its own notion of equality?By convention: an `Equal` method the caller must remember to use. `time.Time` and `bytes.Equal` are the models. The compiler cannot redirect `==` to it, so a struct whose fields include a cache, a monotonic reading or a pointer can compare unequal for values that are semantically the same — which is exactly why those types document the method prominently.
- Are there Go types where == is not merely wrong but illegal or fatal?Yes. A struct containing a slice, map or function field is not comparable, and `==` on it fails to compile. Worse, comparing two interface values whose dynamic type is uncomparable compiles fine and panics at run time — boxing the value moves the check from the compiler to the runtime.
- Doesn't the z.Add(x, y) convention in math/big buy something back?It does: the receiver is an explicit destination, so the caller decides where the result lands and can reuse a big.Int across a loop instead of allocating per operation. An overloaded `+` would have to allocate a fresh value every time. The verbosity buys allocation control, which matters in the tight loops such packages exist for.
saying these in an interview costs you the question
- Says Go supports overloading through interfaces or embedding
- Claims == on a struct calls a user-defined method
- Assumes two time.Time values for one instant are always ==
- Thinks type Celsius float64 lets you define a custom +
- Denies that math-heavy Go code pays a real readability cost