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