Golang fun : adding methods to primitive types

Submitted by Frederic Marand on

Go syntax has some funny peculiarities which are not immediately obvious, like methods on primitive types. Let's have a look.

Primitive wrapper types

Anyone with a few hours of Go is familiar with the basic object notation, like this example:

package main

import "fmt"

type Int struct {
  value int
}

// Imagine this is an actually complex and useful function instead.
func (i Int) Add(j Int) Int {
  return Int{i.value + j.value}
}

func main() {
  i := Int{5}
  j := Int{6}
  fmt.Println(i.Add(j))
}

Try it on http://play.golang.org/p/GUgWEz_nXd

Looking for simplicity

The problem, of course, is that not only is this useless, it is also ugly, because we are wrapping a primitive type in a useless struct. What we probably would like would be something like this, like one can do in Ruby, where all types are objects which can receive methods:

package main

func (i int) Add(j int) int {
  return i + j
}

func main() {}

But if we try this, the Go compiler complains, as you can see on http://play.golang.org/p/95YSh_rmBo

prog.go:3: cannot define new methods on non-local type int

A solution

Now, Go has the ability to assign types to other types:

type Int int

Unlike in some languages where types are first-class objects, this is not aliasing: the two types will be different when compared, but offer the same capabilities. And this brings us the expected capability:

package main

import "fmt"

type Int int

func (i Int) Add(j Int) Int {
  return i + j
}

func main() {
  i := Int(5)
  j := Int(6)
  fmt.Println(i.Add(j))
  fmt.Println(i.Add(j) + 12)
}

Run it on http://play.golang.org/p/Y49wr_gYPo

There we have it : we added a method on a type which is a copy of a primitive type. No struct boxing, no unboxing in the method.

Why it matters

The real beauty of this is actually not the ability to attach the method, although this is definitely satisfying from a conceptual point of view, bringing consistency with an approach opposite to that or Ruby, it is rather the code in the Add() method itself, and the last line in main() are actually the beauty of the thing : since our local Int type is actually just a name for the intrinsic int, it still support the builtin operators like naked ints: no explicit casts needed to use the aliased variable in an expression.