switch xxx.(type) 是一种特殊的 switch 使用,用于判断实现了某一接口的变量的类型是什么。

首先上 Demo 代码

package main

import "fmt"

type interfaceA interface {
    funcA() string
    funcB() string
}

type structA struct {
    MemberA string
    MemberB string
}

type structB struct {
    MemberA string
    MemberB string
}

type structC struct {
    MemberA string
    MemberB string
}

var _ interfaceA = (*structA)(nil)
var _ interfaceA = (*structB)(nil)

func (sa *structA) funcA() string {
    return sa.MemberA
}

func (sa *structA) funcB() string {
    return sa.MemberB
}

func (sb structB) funcA() string {
    return sb.MemberA
}

func (sb structB) funcB() string {
    return sb.MemberB
}

func main() {
    var testA interfaceA
    // testA = structB{}
    testA = &structA{}
    // testA = structA{} //编译器会报错
    testA = &structB{}
    switch testA.(type) {
    case *structA:
        fmt.Println("im structA")
    case structB:
        fmt.Println("im structB")
    case *structB:
        fmt.Println("im structB ptr")
    // case structC: // 编译器会报错,因为 structC 没有实现 interfaceA
    //     fmt.Println("im structC")
    }
}
var _ interfaceA = (*structA)(nil)
var _ interfaceA = (*structB)(nil)

这里是两个约束, 确保结构体 structA 和结构体 structB 都已经完成了对接口 interfaceA 的实现。

// testA = structA{} //编译器会报错

这是因为实现了 structA 中的方法 funcA 和 funcB,他们的接收器中都为指针, 所以不能使用值。
这里又涉及了另一个知识点:

使用值时,只包含值方法集,不包含指针方法集
使用指针时,包含值方法集,也包含值方法集(就是全部都包括)

这就是为什么在上文demo中,可以使用 testA = structB{} 也可以使用 testA = &structB{}
因为 structB 中的方法,其接收器都为值。所以使用指针 &structB{} 时,可以直接调用方法 funcA 和 funcB