1. 『每周譯Go』Go 1.18 泛型的一些技巧和困擾

        共 3506字,需瀏覽 8分鐘

         ·

        2021-11-19 09:12

        截至2021年11月17日,社區(qū)可能還沒有使用 Go 1.18 泛型功能的緩存庫。

        我嘗試在這里實(shí)現(xiàn)了第一個 Go 1.18 泛型的緩存庫。如果你能夠給的 GitHub 加個 Star,我會感到非常高興。https://github.com/Code-Hex/go-generics-cache

        在這篇文章中,我將介紹我在開發(fā)這個緩存庫時遇到的關(guān)于 Go 泛型的一些情況,以及我發(fā)現(xiàn)的一些技巧和困擾。


        對任何類型都返回零值

        你經(jīng)常會寫一些返回 anyerror的代碼,比如說下面這樣。當(dāng)一個函數(shù)發(fā)生錯誤時,你會寫一些返回零值和錯誤的代碼,但現(xiàn)在你需要換一種思維方式。

        func?Do[V?any](v?V)?(V,?error)?{
        ????if?err?:=?validate(v);?err?!=?nil?{
        ????????//?What?should?we?return?here?
        ????}
        ????return?v,?nil
        }

        func?validate[V?any](v?V)?error

        假設(shè)你在這里寫return 0, err。這將是一個編譯錯誤。原因是any類型可以是int類型以外的類型,比如string類型。那么我們應(yīng)該怎么做呢?

        讓我們用類型參數(shù)的V聲明一次變量。然后你可以把它寫成可編譯的形式,如下:

        func?Do[V?any](v?V)?(V,?error)?{
        ????var?ret?V
        ????if?err?:=?validate(v);?err?!=?nil?{
        ????????return?ret,?err
        ????}
        ????return?v,?nil
        }

        此外,可以使用帶命名的返回值來簡化單行的書寫。

        func?Do[V?any](v?V)?(ret?V,?_?error)?{
        ????if?err?:=?validate(v);?err?!=?nil?{
        ????????return?ret,?err
        ????}
        ????return?v,?nil
        }

        https://gotipplay.golang.org/p/0UqA0PIO9X8


        不要試圖用約束做類型轉(zhuǎn)換


        我想提供兩個方法,IncrementDecrement。它們可以從go-generics-cache(https://github.com/Code-Hex/go-generics-cache)庫中增加或減少值,如果存儲的值滿足Number約束(https://github.com/Code-Hex/go-generics-cache/blob/d5c3dda0e57b4c533c1e744869032c33a4fc2d9e/constraint.go#L5-L8)。

        讓我們用Increment方法作為一個例子。我最初寫的代碼是這樣的:

        type?Cache[K?comparable,?V?any]?struct?{
        ????items?map[K]V
        }

        func?(c?*Cache[K,?V])?Increment(k?K,?n?V)?(val?V,?_?error)?{
        ????got,?ok?:=?c.items[k]
        ????if?!ok?{
        ????????return?val,?errors.New("not?found")
        ????}

        ????switch?(interface{})(n).(type)?{
        ????case?Number:
        ????????nv?:=?got?+?n
        ????????c.items[k]?=?nv
        ????????return?nv,?nil
        ????}
        ????return?val,?nil
        }

        我在考慮使用值n V的類型來匹配被滿足的約束。如果滿足Number約束,這個方法就會增加,否則什么都不做。

        這將不會被編譯。

        1. Go 不為約束條件提供條件分支
        2. 約束是一個接口,Go 不允許使用接口進(jìn)行類型斷言
        3. n的類型沒有確定,所以+操作是不可能的
        4. 首先,不能保證items的類型與n的類型相同

        為了解決這些問題,我決定嵌入Cache結(jié)構(gòu)。我還定義了一個NumberCache結(jié)構(gòu),可以一直處理Number約束。

        • 繼承 Cache結(jié)構(gòu)體所持有的字段數(shù)據(jù)
        • 處理 Cache的方法
        type?NumberCache[K?comparable,?V?Number]?struct?{
        ????*Cache[K,?V]
        }

        這樣,我們可以保證傳遞給Cache結(jié)構(gòu)的值的類型永遠(yuǎn)是Number的約束。所以我們可以給NumberCache結(jié)構(gòu)添加一個Increment方法。

        func?(c?*NumberCache[K,?V])?Increment(k?K,?n?V)?(val?V,?_?error)?{
        ????got,?ok?:=?c.Cache.items[k]
        ????if?!ok?{
        ????????return?val,?errors.New("not?found")
        ????}
        ????nv?:=?got?+?n
        ????c.Cache.items[k]?=?nv
        ????return?val,?nil
        }

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


        使我困擾的點(diǎn)


        讓我們再看一下Cache結(jié)構(gòu)的定義。

        type?Cache[K?comparable,?V?any]?struct?{
        ????items?map[K]V
        }

        Go 范型被定義為一種帶有約束的語言規(guī)范,這種約束被稱為 comparable。這允許只有類型可以使用 ==!=。

        我覺得這個約束條件讓我很困擾。讓我解釋一下困擾我的原因。

        我定義了一個函數(shù)來比較兩個 comparable 的值。

        func?Equal[T?comparable](v1,?v2?T)?bool?{
        ????return?v1?==?v2
        }

        只允許 comparable 的類型,如果在編譯時將不可比較的類型傳遞給函數(shù),就會導(dǎo)致錯誤。你可能認(rèn)為這很有用。

        然而,根據(jù) Go 的規(guī)范,interface{}也滿足這個可比較的約束。

        如果interface{}可以被滿足,下面的代碼就可以被編譯了。

        func?main()?{
        ????v1?:=?interface{}(func()?{})
        ????v2?:=?interface{}(func()?{})
        ????Equal(v1,?v2)
        }

        這表明func()類型是一個不可比較的類型。但可以通過將其轉(zhuǎn)換為interface{}類型來轉(zhuǎn)換為可比較的類型。

        interface{}類型只有在運(yùn)行時才能知道它是否是一個可比較的類型。

        如果這是一段復(fù)雜的代碼,可能很難被注意到。

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

        我相信我們需要另一個不接受interface{}的可比約束,以便在編譯時注意到。

        這種約束可以由 Go 用戶來定義嗎?目前的答案是不能。

        這是因為comparable約束包含 "可比較的結(jié)構(gòu)體" 和 "可比較的數(shù)組"。

        這些約束目前不能由 Go 用戶定義。因此,我想把它們作為 Go 規(guī)范來提供。

        我還為此創(chuàng)建了一個提案,如果你也認(rèn)同這個說法,請在 GitHub issue 上給我??,我將不勝感激。https://github.com/golang/go/issues/49587


        文中提到的鏈接

        • comparable https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#comparable-types-in-constraints



        原文信息

        原文地址:https://dev.to/codehex/some-tips-and-bothers-for-go-118-generics-lc7

        原文作者:Kei Kamikawa

        本文永久鏈接:https://github.com/gocn/translator/blob/master/2021/w46_some-tips-and-bothers-for-go-118-generics.md

        譯者:Cluas


        想要了解關(guān)于 Go 的更多資訊,還可以通過掃描的方式,進(jìn)群一起探討哦~



        瀏覽 57
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
          
          

            1. 小早川玲子1080绝顶狂 | 国产精品久久久久影院色老大 | 一级A色情 | 欧美一级特黄A片免费观看密森 | 日韩A片一级无码免费 蜜桃 |