为了账号安全,请及时绑定邮箱和手机立即绑定

为什么 Go 中的 maps.Keys() 指定映射类型为 M?

为什么 Go 中的 maps.Keys() 指定映射类型为 M?

Go
喵喵时光机 2022-12-05 11:14:16
我实现了一个函数来获取映射中的键(实际上有几个版本,用于不同的类型),我更新它以在 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符号,并且~只能在类型约束中使用,而不能在实际类型中使用。该理论的唯一问题是:我的版本在此类地图类型上运行良好。所以我不知道它有什么用。
查看完整描述

1 回答

?
繁花如伊

TA贡献2012条经验 获得超12个赞

它在非常罕见的情况下是相关的,在这种情况下,您需要声明一个函数类型的变量(而不调用它),并且您使用另一个在其定义中使用未导出类型的包中的命名映射类型实例化该函数。


当您需要接受和返回定义的类型时,在函数签名中使用命名类型参数最相关,正如您正确猜测的那样,正如@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 版本和/或当这些函数被提升到标准库中而改变。


查看完整回答
反对 回复 2022-12-05
  • 1 回答
  • 0 关注
  • 118 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号