Ivan
Ivan

Reputation: 169

Why does switch match a different type in below code sample in golang

Link: https://play.golang.org/p/69I8PAuoAV

Extract:

package main

import "fmt"

type Stringer interface {
    String() string
}

type fakeString struct {
    content string
}

// function used to implement the Stringer interface
func (s *fakeString) String() string {
    return s.content
}

func printString(value interface{}) {
    switch str := value.(type) {
    case string:
        fmt.Println(str)
    case Stringer:
        fmt.Println(str.String())
    }
}

func main() {
    s := &fakeString{"Ceci n'est pas un string"}
    printString(s)
    printString("Hello, Gophers")

}

The printString(s) function call when reaching case matches the case Stringer part. s is of type *fakeString not Stringer. Why does it match Stringer.

I did a fmt.Println(reflect.TypeOf(str)) and it confirmed the type as *main.fakeString

Upvotes: 1

Views: 1473

Answers (2)

cizixs
cizixs

Reputation: 13981

This is interface satisfaction in golang.

There are two kinds of type in golang: interface type and concrete type.

  • concrete type works exactly as you expected in switch: the variable is an instance of the given type
  • interface type on the other hand works differently, it checks if the variable satisfies the interface.

Hum, safisfies? How does it work?

A (concrete) type satisfies an interface if it possesses all the methods the interface requires.

-- the golang programming language

In the case, Stringer is an interface, declaring only one method: String() string. And fakeString satisfies it by having its own String() string method.

Two things to note however:

  1. concrete type can have many other methods, it still satisfies the interface if it possesses all methods declared in interface
  2. the methods order does not matter, all that matters is the set of methods.

Upvotes: 1

Yandry Pozo
Yandry Pozo

Reputation: 5123

In Go to satisfy an interface all you have to do is implement the methods of that interface nothing else. In this case to have a behavior as a Stringer you have to implement String() string, so the type fakeString behaves as a Stringer because implement that method; in terms of OOP we use interfaces just to define behavior not types or values. So you may be implementing Go interfaces all day without knowing, but not big deal, it only matters when you have to satisfy behaviors.

Actually in a real project you may want to add a default case to your switch in the method printString, to detect when the interface{} is something else, like this:

func printString(value interface{}) {
    switch str := value.(type) {
    case string:
        fmt.Println("string", str)
    case Stringer:
        fmt.Println("Stringer", str.String())
    default:
        fmt.Printf("unexpected type: %T value: '%v'", str, str)
    }
}

// main:
printString(2)

https://play.golang.org/p/pmxSXfUu4e

Upvotes: 4

Related Questions