在下面的例子中foo和bar基本上屬于同一型別:map[uint32]string.
盡管如此,go1.18beta 抱怨說:M2 does not match map[K]V.
甚至有可能equal接受這兩個地圖嗎?我是否需要更改equal地圖本身的簽名或宣告?
package main
import "fmt"
func equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool {
if len(m1) != len(m2) {
return false
}
for k, v1 := range m1 {
if v2, ok := m2[k]; !ok || v1 != v2 {
return false
}
}
return true
}
type (
someNumericID uint32
someStringID string
)
func main() {
foo := map[uint32]string{
10: "bar",
}
bar := map[someNumericID]someStringID{
10: "bar",
}
if equal(foo, bar) == true {
fmt.Println("Maps are the same")
} else {
fmt.Println("Maps are not the same")
}
}
uj5u.com熱心網友回復:
甚至有可能接受這兩個地圖嗎?
是的,但是您必須區分鍵和值型別,因為它們不一樣。這就是固定函式的樣子:
func equal[K1, K2 ~uint32, V1, V2 ~string](m1 map[K1]V1, m2 map[K2]V2) bool {
if len(m1) != len(m2) {
return false
}
for k, v1 := range m1 {
if v2, ok := m2[K2(k)]; !ok || V2(v1) != v2 {
return false
}
}
return true
}
特別是,鍵和值型別引數都被限制在各自的近似元素~uint32中~string,以便允許在函式體中m2[K2(k)]進行轉換。V2(v1)這是比較不同型別但具有相同基礎型別的值(包括地圖索引)所必需的。
上述解決方案放棄了型別引數M1和M2映射型別——由于似乎是編譯器錯誤;有關詳細資訊,請參閱注釋 - 但由于您實際上并沒有在函式體中使用這些型別,也沒有在回傳值中使用它們,因此它們不是嚴格需要的。
游樂場:https ://gotipplay.golang.org/p/Y8C_8ilsXUg
如果您想了解第一個示例失敗的原因,這里有一個細分。語言規范中的相關段落是Type inference。
- 在
equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2)中,型別 paramsM1和M2約束相同~map[K]V。 - 當您在沒有顯式實體化的情況下呼叫該函式時,編譯器會嘗試從所提供引數的型別推斷型別引數。簡而言之,它從推斷
K和,所以結果在哪里。VM1equal(foo, bar)foomap[uint32]stringK = uint32V = string - 然后實體化的約束是
M1, M2 ~map[uint32]string - 現在沒有更多的型別引數可以推斷,所以它只是
bar針對實體化的約束進行型別檢查。底層 (~) 型別是否bar與 相同map[uint32]string?不,即使keys和vals的底層型別相同,整個map的底層型別也是exactlymap[someNumericID]someStringID。 equal使用 args實體化foo并bar失敗。
如果您不依賴型別推斷,而是equal使用顯式型別引數進行實體化,這將變得更加明顯。通過僅指定M1and M2(記住它們具有相同的約束):equal[map[uint32]string, map[uint32]string](foo, bar)thenbar顯然不匹配。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/426085.html
