新提案:Go 泛型玩出花來了,switch type 登場!

煎魚發表於2021-12-17

大家好,我是煎魚。

前面寫過好幾篇 Go 泛型的語法、案例介紹,新的一手 Go 訊息。實際上,隨著一些提案被接受,新的提案也逐漸冒出。

這不,我發現有了泛型後,大家可以更進一步玩出花來了。看到了一個 ”新“ 提案《proposal: spec: generics: type switch on parametric types》,講的就是增加泛型後的引數型別上的型別開關訴求。

跟著煎魚一起掌握新的 Go 知識吧!

新提案

新的提案內容是希望增加一個新的變種語句,允許在 switch 語句中使用泛型時,能夠進一步便捷的約束其型別引數。

例如:

switch type T {
case A1:
case A2, A3:
   ...
}

也就是 switch-type 語句的 T 型別可以是一個泛型的型別參,case 所對應的的型別可以是任何型別,包括泛型的約束型別。

假設型別 T 的型別有可能是以下:

interface{
    C
    A
}

可以藉助泛型的近似元素來約束:

    interface{
        C
        A1 | A2 | ... | An
    }

甚至還可以在 case 上有新的寫法:

case interface {~T}:

在支援泛型後,switch 在 type 和 case 上會存在很多種可能性,需要進行具體的特性支援,這個提案就是為此出現。

實際案例

案例一:多型別元素

type Stringish interface {
    string | fmt.Stringer
}

func Concat[S Stringish](x []S "S Stringish") string {
    switch type S {
    case string:
        ...
    case fmt.Stringer:
        ...
    }
 }

型別 S 能夠支援 string 和 fmt.Stringer 型別,case 配套對應實現。

案例二:近似元素

type Constraint interface {
    ~int | ~int8 | ~string
}

func ThisSyntax[T Constraint]( "T Constraint") {
    switch type T {
    case ~int | ~int8:
        ...
    case ~string:
        ...
    }
}

func IsClearerThanThisSyntax[T Constraint]( "T Constraint") {
    switch type T {
    case interface{~int | ~int8 }:
        ...
    case interface{ ~string }:
        ...
    }
}

型別 T 可能有很多型別,程式中用到了近似元素,也就是基礎型別是 int、int8、string,這些型別中的任何一種都能夠滿足這個約束。

為此,switch-type 支援了,case 也要配套支援該特性。

爭議點

看到這裡可能大家也想到了,這個味道很似曾相識,好像某個語法能夠支援。因此,這個提案下最有爭議的,就是與原有的型別斷言的重複。

原有的型別斷言如下:

switch T.(type) {
case string:
   ...
default:
   ...
}

新的型別判別如下:

switch type T {
case A1:
case A2, A3:
   ...
}

這麼咋一看,其實型別斷言的完全可以取代新的,那豈不是重複建設,造輪子了?

其實是沒有完全取代的。差異點如下:

type ApproxString interface { ~string }

func F[T ApproxString](v T "T ApproxString") {
    switch (interface{})(v).(type) {
    case string:
        fmt.Println(v)
    default:
        panic("腦子沒進煎魚")
    }
}

type MyString string

func main() {
    F(MyString("腦子進煎魚了"))
}

看出來差別在哪了嗎,答案是什麼?

答案是:會丟擲恐慌(panic)。

你可能糾結了,問題出在哪裡?這傳入的 ”腦子進煎魚了“ 的型別是 MyString,他的基礎型別是 string 型別,也滿足 ApproxString 型別的近似型別 ~string 的要求,怎麼就不行了...

根本原因是因為他的型別是 interface,而非 string 型別。所以走到了 defalut 分支的恐慌。

總結

今天給大家介紹了 Go 泛型的最新訊息,在上一個提案被合併後,該提案也有一些新的動靜。不過 Go 官方表態,會等熟練掌握泛型實踐後,再繼續推動該提案。

我相信,原有的 switch.(type)switch type 很大概率在 Go 底層會變成同一個邏輯塊處理,再逐漸過渡。

這個提案的目的還是為了解決若干引入泛型後,所帶入的 BUG/需求,正正是需要新的語法結構來解決的。

你對此有什麼看法呢,歡迎在評論區留言和交流:)

若有任何疑問歡迎評論區反饋和交流,最好的關係是互相成就,各位的點贊就是煎魚創作的最大動力,感謝支援。

文章持續更新,可以微信搜【腦子進煎魚了】閱讀,本文 GitHub github.com/eddycjy/blog 已收錄,學習 Go 語言可以看 Go 學習地圖和路線,歡迎 Star 催更。

相關文章