golang genericsでswitch-caseをする

はじめに

Go言語では1.18でgenericsの機能が入ってくるので勉強していたのですが引数に型パラメータがあった場合どのようにswitchするのか気になったので備忘録を残します

引数の型が interface{}のとき

proposalに書いてるswitch文を見てみるとcaseの部分で型パラメータが使われている点以外、引数は interface{}になっており、普段go言語を書いている通りの記述になると思います

Generic types as type switch cases - Type Parameters Proposal

type Mystruct struct {
    fieldA string
}

func Switch[T any](v interface{}) (T, bool) {
    switch v := v.(type) {
    case T:
        return v, true
    default:
        var zero T
        return zero, false
    }
}

func main() {
    x, ok := Switch[string]("string")
    if ok {
        fmt.Println(x)  // string
    }

    y, ok := Switch[Mystruct](Mystruct{fieldA: "a"})
    if ok {
        fmt.Println(y) // {a}
    }
}

https://gotipplay.golang.org/p/nQJNsMiwkzh

引数の型が型パラメータの場合

では、関数の引数の型が型パラメータだった場合にどのようにswitchを使うのかをprosalから探してみます

サンプルコードがありました。(※少しだけコードを変更しています)
Identifying the matched predeclared type - Type Parameters Proposal

type Float interface {
    ~float32 | ~float64
}

func NewtonSqrt[T Float](v T) (int, T) {
    var iterations int
    switch (interface{})(v).(type) {
    case float32:
        iterations = 4
    case float64:
        iterations = 5
    default:  // panic: unexpected type main.MyFloat
        panic(fmt.Sprintf("unexpected type %T", v))
    }
    return iterations, v
}

type MyFloat float32

func main() {
    iterations, x := NewtonSqrt(MyFloat(64))

    fmt.Println(iterations)
    fmt.Println(x)
}

https://gotipplay.golang.org/p/te5gOiFyx2I

proposalを読んでみると以下のように書いてありました

The design doesn't provide any way to test the underlying type matched by a ~T constraint element.

つまり、現時点 (2022/1/29)ではunderlying typeのswitch文は書けそうにありません。
しかし、あくまでunderlying typeのときの話なので、今度はMyFloatではなくfloat型を関数に渡してみた結果無事に結果が表示されました。

サンプルコード

最後に

type switchに関するproposalが出ているので、将来的にやりたいことはできるのではと思っています

これからもproposalやリリース情報は追った方がよさそうです

github.com