Understanding Go's Type Aliases

January 7, 2022   

Notes from justforfunc #18: understanding Go’s type aliases

Go 1.9 introducted type alaises

The only abstract type that exists are interfaces. All other types (int, string, etc) are concrete types.

Concrete Type Conversions

Given:

type celsius float64

func (c celsius) String() string {
  return fmt.Sprintf("%.2f C", c)
}

type temperature celsius

func main() {
  c := celsius(10.0)
  fmt.Println(c)

  t := temperature(15.0)
  fmt.Println(t)
}

In the above code temperature does not have a receiver function of String() as expected.
But what if we wanted it to?

We could do the following:

type celsius float64

func (c celsius) String() string {
  return fmt.Sprintf("%.2f C", c)
} 

type temperature struct {
  celsius
}

func main() {
  c := celsius(10.0)
  fmt.Println(c)
  
  t := temperature{c}  // NOTE the {}
  fmt.Println(t)
} 

This is called struct embedding. “we are creating temperature that has a celisus” It keeps all the methods that the embedded type has.

Type conversions is only doable explicitly UNLESS one of the types is a name.

Example of implicit conversion

type Func func()

func run(f Func) {
  f()
}

func hello() { fmt.Println("hello") }

func main() {
  fmt.Printf("%T\n", hello)
  run(hello)
}

In the above example the type of hello is not named. It is not a named type. This will print

func() hello

But if we name the func

type Func func()

func run(f Func) {
  f()
}

func hello() { fmt.Println("hello") }

type anotherFunc Func

func main() {
  var f anotherFunc = hello
  fmt.Printf("%T\n", hello)
  //run(hello) //< fails to compile
} 

Results in: main.anotherFunc

It is no longer an anonymous function so the implicit conversion fails.

abstract type conversion

Example


type stringer fmt.Stringer

func main() {
  var ms stringer
  var s fmt.Stringer

  fmt.Println(ms == s)  // true, these types have exactly the same methods

  ms = s //works as long as we match the interface
  s = ms //works as longa s we match the interface
}

type aliases


type celsuis float64

func(c celsius) String() string {
  return fmt.Sprintf("%.2f C", c)
}

type temperature = celsius // The equality causes a type alias

func main() {
  c := celsius(10.0)
  fmt.Println(c)    // 10.00 C - as expected String() overloaded
  fmt.Printf("%T\n", c) // main.celsius

  t := temperature(c)
  fmt.Println(t)   // 10.00 C  - thanks to the alias
  fmt.Printf("%T\n", t) // main.celsius - Temp is a ALAIS for celsius
}

We’ve given a new name to the same type. temperature is not a new type.

Why do type alais’s exist?

The main reason for type alias’s is for refectoring.

An example with the Context package is shared.

In the context package when built with Go 1.9 it uses type alais’s which allows for simplier refactoring by the users to use the alaises