A Quirk Regarding Type Assertions and Switch Statements in Go

A Quirk Regarding Type Assertions and Switch Statements in Go

During my journey of learning Go, I've come across a confusing yet interesting quirk regarding type assertions.

I've learned that when running a switch statement on a value's type, you can match a type that is different than the value's declared type if the value's underlying type satisfies the matched type.

That may might have been a mouthful, but I'll explain exactly what I mean in great detail.

Take the following code for example:

package main

import "fmt"

type Color struct {
    name string
}

type Join interface {
    Mix(o Color)
}

type Combine interface {
    Mix(o Color)
}

func (c *Color) Mix(otherColor Color) {
    fmt.Printf("%v mixed with %v = %v-%v!", c, otherColor, c, otherColor)
}

func main() {
    var blue Combine = &Color{"blue"}

    switch v := blue.(type) {
    case Join: 
        fmt.Printf("%v is a Join.", v)
    case Combine: 
        fmt.Printf("%v is a Combine.", v)
    }
}

Here, we have a struct Color and two interfaces, Join and Combine, that both contain the same method signature, Mix(o Color).

Below these definitions, we define a function Mix whose receiver type is *Color. This means that *Color satisfies both the Join and Combine interfaces due to its implementation of the Mix method.

Now, here's where things get confusing (and interesting):

func main() {
    var blue Combine = &Color{"blue"}

    switch v := blue.(type) {
    case Join: 
        fmt.Printf("%v is a Join.", v)
    case Combine: 
        fmt.Printf("%v is a Combine.", v)
    }

}

What do you think the output of this code should be? This, right?

&{blue} is a Combine

Wrong! We get this instead:

&{blue} is a Join

But why? The answer lies in what I stated before:

"You can match a type that is different than the value's declared type if the value's underlying type satisfies the matched type."

So let's go over this in greater detail:

Here are the facts:

  • The value against whose type we're matching: blue

  • The value's declared type: Combine

  • The value's underlying type: *Color

  • The type the value matched: Join

Due to the fact that the value's (blue) underlying type (*Color) satisfies the matched type (Join), it will match Join and not the expected type (i.e., the declared type Combine). It's really that simple.

I thought this was an interesting quirk and I'm doing my due diligence by documenting my learning process here so that others may avoid the headache of misunderstanding later on.

Happy coding!

Did you find this article valuable?

Support Matthew Smilansky by becoming a sponsor. Any amount is appreciated!