我實作了一個函式來獲取映射中的鍵(實際上有幾個版本,用于不同的型別),我在 Go 1.18 中更新為使??用泛型。然后我發現實驗庫被擴展為包含該功能,雖然我的實作幾乎相同,但函式宣告有一些我想更好地理解的差異。
這是我最初的通用版本(我重命名了變數以匹配標準庫,以更好地突出顯示實際上是不同的):
func mapKeys[K comparable, V any](m map[K]V) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
這是標準庫版本:
func Keys[M ~map[K]V, K comparable, V any](m M) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
如您所見,主要區別在于額外的M ~map[K]V型別引數,我將其省略并直接用于map[K]V函式的引數型別。我的函式有效,那么為什么我需要經歷添加第三個引數化型別的額外麻煩呢?
當我寫我的問題時,我想我已經找到了答案:能夠在真正是映射的型別上呼叫函式,但沒有直接宣告為這樣,就像可能在這種DataCache型別上一樣:
type DataCache map[string]DataObject
我的想法是,這可能需要~map符號,并且~只能用于型別約束,而不是實際型別。這個理論的唯一問題是:我的版本在這種地圖型別上運行良好。所以我不知道它有什么用處。
uj5u.com熱心網友回復:
tl; dr它與非常罕見的情況相關,您需要宣告函式型別的變數(而不呼叫它),并且您使用另一個包中的命名映射型別實體化該函式,該包在其定義中使用未匯出的型別。
當您需要接受和回傳定義的型別時,在函式簽名中使用命名型別引數最相關,正如您正確猜測的那樣,并且正如@icza在這里回答的關于x/exp/slices包的那樣。
您關于“波浪號型別”只能在介面約束中使用的評論也是正確的。
現在,包中的幾乎所有函式x/exp/maps實際上都不會回傳命名型別M。唯一真正做的是maps.Clone簽名:
func Clone[M ~map[K]V, K comparable, V any](m M) M
然而,由于型別統一,在沒有近似約束的情況下宣告簽名仍然適用于已定義的型別。從規格:~map[K]V
[...],因為定義
D的型別和型別文字L永遠不會等價,統一將 D 的基礎型別與 L 進行比較
還有一個代碼示例:
func Keys[K comparable, V any](m map[K]V) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
type Dictionary map[string]int
func main() {
m := Dictionary{"foo": 1, "bar": 2}
k := Keys(m)
fmt.Println(k) // it just works
}
游樂場:https ://go.dev/play/p/hzb2TflybZ9
附加命名型別引數M ~map[K]V相關的情況是當您需要傳遞函式的實體化值時:
func main() {
// variable of function type!
fn := Keys[Dictionary]
m := Dictionary{"foo": 1, "bar": 2}
fmt.Println(fn(m))
}
游樂場:https ://go.dev/play/p/hks_8bnhgsf
如果沒有M ~map[K]V型別引數,就不可能用定義的型別實體化這樣的函式值。當然,您可以使用K和V單獨實體化您的函式,例如
fn := Keys[string, int]
但是當定義的映射型別屬于不同的包并參考未匯出的型別時,這是不可行的:
package foo
type someStruct struct{ val int }
type Dictionary map[string]someStruct
和:
package main
func main() {
// does not compile
// fn := Keys[string, foo.someStruct]
// this does
fn := maps.Keys[foo.Dictionary]
}
不過,這似乎是一個相當公開的用例。
你可以在這里看到最終的游樂場:https ://go.dev/play/p/B-_RBSqVqUD
但是請記住,這x/exp/maps是一個實驗包,因此簽名可能會隨著未來的 Go 版本和/或這些函式被提升到標準庫中而改變。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/460174.html
