Go, I love you, but you're bringing me down.
Tags: go
Published: 2017-07-24 16:00:39

---
title: Go, I love you, but you're bringing me down.
published: true
description: A few of the nits I have to pick with Go.
tags: golang 
---

I've said it before, and I'll say it again: I really like Go. It's the most fun I've had learning a language since [Python](https://xkcd.com/353/). It's statically typed and compiled – and as a systems-oriented programmer, I never found anything that could really move me away from C because of this – it has a great standard library, it handles code reuse without inheritance beautifully, and those concurrency paradigms have changed my life for ever.

That being said, I quickly had a few issues with some of the language's specifics. And while I've been trying to learn its patterns and conventions as best I can, I'm pretty certain that some of them could be fixed inside the language, and not by using a specific pattern or abstraction. For the sake of [simplicity](https://dave.cheney.net/2017/06/15/simplicity-debt).

As I was reading Russ Cox's [Gophercon 2017 talk](https://blog.golang.org/toward-go2) about Go 2, I decided to take him up on this:

> **We need your help.**
> Today, what we need most is experience reports. Please tell us how Go is working for you, and more importantly not working for you. Write a blog post, include real examples, concrete detail, and real experience. That's how we'll start talking about what we, the Go community, might want to change about Go.

So here are, as a new Go programmer, my 2 cents about what's missing.

## Operator overloading

Got your attention? Good. Because I hate operator overloading. I've been through too many C++ code bases where developers thought it was better to be clever than to write a clean interface. I've had my share of over-enthusiastic coders re-defining fundamental semantics such as the dot(`.`), arrow(`->`) or call(`()`) operators to do very unexpected things. The cost in documentation and on-boarding new developers was huge, and the mental work of switching paradigms when coding made the whole thing quite error-prone. The fact that the stdlib does weird things pretty freely (`std::cout << "Hello, World!"`, anyone?) doesn't really help.

But operators are also a great shorthand for some clearly defined semantics. In other words, if semantics _precede_ the operator, there's a far lesser chance to abuse them. An example would be the `[]` operator for integer-based indexing (such as arrays or slices), or for key-value operations (such as maps). Or the usage of the `range` keyword to iterate over arrays, slices, maps and channels.

One way to make sure these semantics are well respected would be to make the operators as syntactic sugar to well-defined interfaces.

Key-value operator:

```go
struct KeyValueMap interface {
    Value(interface{}) interface{}
    SetValue(interface{}, interface{})
}
```

We could then use the `[]` operator as a shorthand to these functions.

```go
foo := keyVal["bar"]
```

would be equivalent to

```go
foo := keyVal.Value("bar")
```

The objects used as key would need to be hashable or comparable in some way. Perhaps the compiler could enforce the [same rules](https://golang.org/ref/spec#Comparison_operators) it does with maps.

Range iteration could be done similarly:

```go
type Iterator interface {
    Next() (interface{}, bool)
    Reset()
}
```

`Next` would return the next value in the iteration, and a `bool` that would be `true` when the iterator is finished. `Reset` would be used to initialize the iterator at the beginning of the `for` loop.

```go
for v := range it {
```

would be equivalent to

```go
it.Reset()
for !done {
    i, done := it.Next()
    if done {
        break
    }
```

I think this would allow Go developers to implement much more go-ish structures and I can see many cases of memory-efficient iterators that would make use of this clean syntax.

Of course, this kind of breaks the strong typing in Go. Maybe a feature like this would go hand-in-hand with generics?

## Initialization of embedded interfaces

We recently had a case of an issue that took us a while to debug. We were using type embedding to compose different services in one structure. At first we had just one handler.

```go
type FooHandler interface {
    // some `FooHandler` methods
}

type Handler struct {
   FooHandler
}

func main() {
    // Foo is a concrete type implementing FooHandler
    h := Handler{FooHandler: &Foo{}}
    RegisterHandler(h)
```

We embedded a second handler, but forgot to add it in the `Handler` initialization. The error message was pretty cryptic.

```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x10871d6]

goroutine 1 [running]:
main.RegisterHandler(0x1108140, 0x1126ac8, 0x0, 0x0)
```

This was fixed by adding the initialization.

```go
    h := Handler{FooHandler: &Foo{}, BarHandler: &Bar{}}
```

I wish the error message would have been clearer. Such a simple omission shouldn't require 30 minutes and two programmers. Maybe we're just new at Go, but I wish the error would have been more explicit. That or the compiler could warn against using zero values for embedded types. I don't know if there's a case where you would want to embed a `nil` value. This would probably be a breaking change for a few code bases, but the benefit in memory safety could be worth it.

## Closed channels

> **EDIT:** The issue I detailed here was only because of a feature of the language I didn't know of. It was pointed out in the comments and on [Twitter](https://twitter.com/Sajma/status/889573024642584576) (and [here](https://twitter.com/Sajma/status/889589005976776704)). I'll be showing Go way to avoid the problem I had.

In the following program, the final loop loops forever while receiving zero values from both channels, `ca` and `cb`.

```go
func main() {
	// Create a first channel
	ca := make(chan int)
	go func() {
		// Send one integer over the channel every millisecond up to 9
		for i := 0; i < 10; i++ {
			ca <- i
			time.Sleep(1 * time.Millisecond)
		}
		close(ca)
	}()

	// Create a second channel
	cb := make(chan int)
	go func() {
		// Send one integer over the channel every millisecond up to 99
		for i := 0; i < 100; i++ {
			cb <- i
			time.Sleep(1 * time.Millisecond)
		}
		close(cb)
	}()

	// Receive from the first available channel and loop
	for {
		select {
		case n := <-ca:
			fmt.Printf("a%d ", n)
		case n := <-cb:
			fmt.Printf("b%d ", n)
		}
	}
}
```

This problem can be fixed by reading two values from the channel. By using `n, ok := <-ca`, `ok` will be `false` if the channel is closed. Once the channel has been closed, we set it to `nil` as receiving from a `nil` channel blocks.

```go
	// Read from first available channel and loop
	// until both channels are nil
	for ca != nil || cb != nil {
		select {
		case n, ok := <-ca:
			if ok {
				fmt.Printf("a%d ", n)
			} else {
				// If the channel has been closed, set it to nil
				// Receiving from a nil channel blocks, so we know
				// this select branch will be unreachable after this
				ca = nil
			}
		case n, ok := <-cb:
			if ok {
				fmt.Printf("b%d ", n)
			} else {
				cb = nil
			}
		}
	}
```

----

I'm still pretty new to Go, so I'm fairly certain these are either misconceptions on my part, or things that have been tried and abandoned because they didn't work. Feel free to give me any documentation or pointers in the comments.