After 12-plus years in the wild, the Go language is now firmly established as a tool for professional programmers and for building large-scale systems. Although Go is deliberately spare, clean, and efficient, requests for certain features bubble up time and again, even if on closer inspection those features run counter to Go’s underlying philosophies or design goals.
Here are five commonly requested features in Go, all of which you’re not likely to see, or which will only show up in a form that complements the rest what's in Go now.
Many languages have a construction that is essentially an
if/then/else block in a single statement. For example, in Python we can write,
x = condition ? 1 : 0.
The Go FAQ is quite blunt about why the language does not have this feature:
The language’s designers had seen the operation used too often to create impenetrably complex expressions.... A language needs only one conditional control flow construct.
Many Go programmers agree with this assessment, judging from the comments for one proposal for a ternary operator in 2019. Related proposals, such as one for conditional assignment earlier that year, also failed to gain traction.
Sum types or algebraic types
Another feature found in modern languages like Rust is the sum type. Also known as an algebraic type or variant type, a sum type is a variable that can be any one of several possible non-nullable types. In some languages, a sum type can be used as a way to return either a valid value or an error value from an operation.
Some Go programmers like the idea of having such types. In theory, a non-nullable type would make it easier to handle checks for
nil. Sum types would also make error handling more graceful.
However, proposals for these type system enhancements haven’t taken off. Go’s
interface type has become the idiomatic way to implement many of the same mechanisms as would be handled by sum types.
The Go FAQ has this to say about the issue:
We considered adding variant types to Go, but after discussion decided to leave them out because they overlap in confusing ways with interfaces. What would happen if the elements of a variant type were themselves interfaces?
Immutable structure types
Many languages support the idea of an immutable structure—not just a scalar variable that serves as a constant (such as an integer or a float) but a higher-order collection of elements that is immutable.
Go allows the programmer to create constants, but only for scalar types, such as integers or floats. It’s not possible in Go to create an immutable slice of integers, for instance. The only way to have anything like immutable behaviors in Go is to make copies of things and operate on the copies. That can be slow, and it puts an additional burden on the developer to ensure they are in fact copying and not operating on originals.
Proposals have surfaced to create immutable types in Go 1.x. One of the most comprehensive proposed giving each type an immutable counterpart, and offered suggestions for implementing immutable interface methods. Objections to the proposal raised “major flaws” such as
const poisoning, whereby adding constant behaviors in one part of a program required all dependant code paths to also implement them, or later become difficult to undo. One longtime user of the D language described their experience with a similar feature added to that language, and how it complicated things unpredictably.
Default argument values
Another feature you find in many other programming languages is default argument values for methods or functions. Here a method is initialized with default values, so if you don’t supply arguments when you call it, the method will provide the defaults.
Go has no feature like this, as its designers considered it to be against the language’s philosophy. One objection is that default argument values encourage poor API design, because they allow a function’s arguments to be changed after being established. Proposals to add this feature to the language as recently as 2020 have been declined on the same grounds.
A potential substitute for default argument values is variadic functions, which are functions that can take any number of the same type of arguments. Variadic functions would allow for more arguments to be passed to the function later on, and for arguments not provided to be automatically assigned values. But all this comes at the cost of not being explicitly described in the function’s signature, which runs counter to Go’s philosophy.
Conventional error handling
Error handling in Go works by having functions return both a regular value and an error value. If the error value is anything but
nil, an error has occurred and it’s up to the caller to handle it.
It’s a simple approach but also repetitive and verbose, sometimes excessively so. Go’s
panic/recover pattern, the closest thing the language has to exceptions, isn’t meant to be used for regular error checking, but to rescue a crashing program.
An open goal for a future version of Go is a better way to handle errors, but so far little has arisen that seems likely to replace the common
if err != nil boilerplate.
One proposal was to replace the common pattern (i.e., executing a function, obtaining its values along with an error, and returning the error back up the call chain if it’s non-
nil) with a built-in error check function called
try. But many Go users resisted the idea for many reasons, including the reason that a terser construction is not a solution to the problem, so the
try proposal was declined.
Any changes to how Go handles errors will first need to satisfy its existing users, many of whom see Go’s verbosity and explicitness as a benefit and not a burden.