Golang中使用maps.Copy时遇到结构体作为键的编译错误问题
Golang中使用maps.Copy时遇到结构体作为键的编译错误问题 我基于泛型实现了一个Set,当使用基础类型作为Set元素时一切正常,但当我使用结构体作为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{}{} },则可以成功编译。
我觉得这很奇怪,有人能解释一下吗?我也在Stack Overflow上提了一个问题:go 1.18 generic compile error when use maps.Copy on map with struct key - Stack Overflow
更多关于Golang中使用maps.Copy时遇到结构体作为键的编译错误问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
blackgreen 已经给出了一个很棒的答案,请查看指向 Stackoverflow 问题的链接。
但我不知道是否有办法手动关闭这个主题。
更多关于Golang中使用maps.Copy时遇到结构体作为键的编译错误问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这个编译错误是由于Go 1.18中泛型实现的一个已知问题导致的。当结构体作为comparable类型键时,maps.Copy函数在处理某些结构体类型时会产生类型推断问题。
问题出现在maps.Copy的泛型实现中,它无法正确处理包含多个字段的结构体作为map键的情况。这是一个编译器层面的bug,在后续的Go版本中已经修复。
解决方案:使用显式的循环代替maps.Copy
func (s Set[T]) AddSet(another Set[T]) {
for v := range another {
s[v] = struct{}{}
}
}
或者使用类型断言来绕过编译器问题
func (s Set[T]) AddSet(another Set[T]) {
// 将map转换为interface{}再处理
src := map[T]struct{}(another)
dst := map[T]struct{}(s)
for k := range src {
dst[k] = struct{}{}
}
}
完整的工作示例:
package main
import (
"fmt"
)
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) // 输出: map[{1 1}:{} {2 2}:{} {3 3}:{} {4 4}:{} {5 5}:{}]
}
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]) {
for v := range another {
s[v] = struct{}{}
}
}
根本原因分析:
这个bug与Go编译器的类型推断机制在处理泛型结构体键时的内部实现有关。当结构体包含多个字段时,编译器在生成中间代码时会产生类型不匹配的错误。这个问题在Go 1.19及更高版本中已经得到修复。
验证修复:
// Go 1.19+ 中可以正常使用maps.Copy
package main
import (
"fmt"
"golang.org/x/exp/maps"
)
type Key struct {
A, B, C int // 多个字段在1.19+中也能正常工作
}
func main() {
s1 := map[Key]string{
{1, 2, 3}: "value1",
}
s2 := map[Key]string{
{4, 5, 6}: "value2",
}
maps.Copy(s1, s2)
fmt.Println(s1) // 输出: map[{1 2 3}:value1 {4 5 6}:value2]
}
建议升级到Go 1.19或更高版本以获得完整的泛型支持修复。如果必须使用Go 1.18,请使用显式循环作为替代方案。

