Go Optimizations 101, Go Details & Tips 101 and Go Generics 101 are all updated for Go 1.25 now. The most cost-effective way to get them is through this book bundle in the Leanpub book store.

Some (Real) Go Subtleties

I just read an article: 15 Go Subtleties You May Not Already Know. After reading that article, I just feel the title is some weird/improper. Because all the so-called subtleties mentioned in that article are what a quanlified Go programer should know about.

Go indeed has many subtleties. Even many professional Go programmers are not aware of some of them. Here, this article shows several of them (all of them are from the Go Details & Tips 101 book).

Comparing two pointers to the same zero-size value might result either true or false, even during the same program execution

For example. What doesn't the following program print? Nothing, true, or false?

package main

var a, b [0]int
var p, q = &a, &b

func main() {
	if (p == q) {
		p, q = &a, &b
		println(p == q)
	}
}

It prints false (with the official Go toolchain). Surprised? But it looks the behavior doesn't violate Go specification.

for true {...} and for {...} are not eqivalent

For example, the function foo shown below compiles, but bar doesn't.

func foo() int {
  for {}
}

func bar() int {
  for true {}
}

When using the make built-in function to create a map, the second argument (cap) can be negative

For example, the following code compiles.

package main

var n = -100

func main() {
    var m = make(map[int]bool, n)
    m[123] = true;
}

A negative cap argument is equivalent to omitting it.

aConstantString[:] is not a constant

Here is an example to prove it.

package main

const s = "zigo 101" // len(s) == 8
var a byte = 1 << len(s) / 128
var b byte = 1 << len(s[:]) / 128

func main() {
	println(a, b) // 2 0
}

Some Go programmers might be surprised by this result. To understand the result well, we need to know about the next subtlety.

If the left operand of a non-constant bit-shift expression is untyped, then its type is determined as the assumed type of the expression

For example, the following program prints 0 0 2.

package main

const N = 8
var n = N

func main() {
	var x byte = 1 << n / 128
	var y = byte(1 << n / 128)
	var z byte = 1 << N / 128
	
	println(x, y, z) // 0 0 2
}

In the first two declarations, the types of 1 are both assumed as byte. So the two declarations are both eqivalent to the following line, in which the run-time expression byte(1) << n overflows (and is truncated to 0).

    ... = byte(1) << n // 128

The last declaration is eqivalent to the following line. The expression 1 << N / 128 is evaluated at compile time. In the expression, 1 is treated as untyped int value.
   var z = 2

(more articles ↡)

The Go 101 project is hosted on Github. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.

If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101.

Tapir, the author of Go 101, has been on writing the Go 101 series books and maintaining the go101.org website since 2016 July. New contents will be continually added to the book and the website from time to time. Tapir is also an indie game developer. You can also support Go 101 by playing Tapir's games (made for both Android and iPhone/iPad):
Individual donations via PayPal are also welcome.

Articles in this book: