英文:
go 1.18 generic compile error when use maps.Copy on map with struct key
问题
我实现了一个基于泛型的集合(Set),一切都很好,直到我将结构体作为集合元素而不是基本类型。我遇到了一个编译错误。
Go版本:go version go1.18 windows/amd64
下面的代码在AddSet
函数中无法编译通过。
package main
import (
"fmt"
"golang.org/x/exp/maps"
)
type Key struct {
A, B int
}
func main() {
s := SetOf(
Key{1, 1},
Key{2, 2},
Key{3, 3},
)
s.AddSet(SetOf(
Key{3, 3},
Key{4, 4},
Key{5, 5},
))
fmt.Println(s)
}
type Set[T comparable] map[T]struct{}
func SetOf[T comparable](vs ...T) Set[T] {
s := Set[T]{}
for _, v := range vs {
s[v] = struct{}{}
}
return s
}
func (s Set[T]) AddSet(another Set[T]) {
maps.Copy(s, another)
}
运行时出现以下错误:
> go run .\main.go
# command-line-arguments
.\main.go:19:10: cannot use &.autotmp_29 (type *struct { A int; B int }) as type *Key in argument to runtime.mapassign
<autogenerated>:1: cannot use &.autotmp_12 (type *struct { A int; B int }) as type *Key in argument to runtime.mapassign
- 如果
Key
只有一个字段,它可以成功编译。 - 如果我使用
for v := range another { s[v]=struct{}{} }
,它可以成功编译。
我认为这很奇怪,有人可以解释一下吗?
英文:
I implemented a Set based on generic, and everything ok until i use struct as Set element instead of base type. I got an compliation error.
go version: go version go1.18 windows/amd64
Below code is failed to complie in function AddSet
.
package main
import (
"fmt"
"golang.org/x/exp/maps"
)
type Key struct {
A, B int
}
func main() {
s := SetOf(
Key{1, 1},
Key{2, 2},
Key{3, 3},
)
s.AddSet(SetOf(
Key{3, 3},
Key{4, 4},
Key{5, 5},
))
fmt.Println(s)
}
type Set[T comparable] map[T]struct{}
func SetOf[T comparable](vs ...T) Set[T] {
s := Set[T]{}
for _, v := range vs {
s[v] = struct{}{}
}
return s
}
func (s Set[T]) AddSet(another Set[T]) {
maps.Copy(s, another)
}
when run it:
> go run .\main.go
# command-line-arguments
.\main.go:19:10: cannot use &.autotmp_29 (type *struct { A int; B int }) as type *Key in argument to runtime.mapassign
<autogenerated>:1: cannot use &.autotmp_12 (type *struct { A int; B int }) as type *Key in argument to runtime.mapassign
- if
Key
only has 1 field, it can be compiled successful. - if i use
for v := range another { s[v]=struct{}{} }
, it can be compiled successful.
i think it's strange, can someone explain please?
答案1
得分: 2
看起来是一个编译器错误,可以在 Go 1.19 中修复,并且已经回溯到 Go 1.18.2。
如果你使用的是旧版本,我建议你放弃使用 maps
包,像你之前尝试的那样手动处理。只需要一个简单的循环:
func (s Set[T]) AddSet(another Set[T]) {
for k := range another {
s[k] = struct{}{}
}
}
@icza 的评论中也提到了将命名的 map 类型显式转换为其底层类型的方法:
maps.Copy(map[T]struct{}(s), another)
如果你使用的是期望多个 map 类型参数的函数(具有相同的约束),如 maps.Equal
或 maps.EqualFunc
,你需要转换两个参数:
func (s Set[T]) Compare(another Set[T]) bool {
// 函数签名是 Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool
return maps.Equal(map[T]struct{}(s), map[T]struct{}(another))
}
似乎已经复现了这个崩溃,也使用了参数化的 map 类型,并实例化了长度大于等于 2 的数组。
英文:
It looks like this compiler error. It is fixed in Go 1.19 and backported to Go 1.18.2.
If you are on an older version, I'd recommend simply forgoing the maps
package and doing things by hand, as you already tried. It's just a simple loop:
func (s Set[T]) AddSet(another Set[T]) {
for k := range another {
s[k] = struct{}{}
}
}
@icza's comment of explicitly converting the named map type to its underlying type also works:
maps.Copy(map[T]struct{}(s), another)
In case you use functions that expect more than one map type parameter (with the same constraints), as maps.Equal
or maps.EqualFunc
, you have to convert both arguments:
func (s Set[T]) Compare(another Set[T]) bool {
// signature is Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool
return maps.Equal(map[T]struct{}(s), map[T]struct{}(another))
}
<hr>
It seems the crash was reproduced also with parametrized map types instantiated with arrays with len >= 2.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论