当在具有结构体键的映射上使用maps.Copy时,会出现go 1.18通用编译错误。

huangapple go评论72阅读模式
英文:

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 (
	&quot;fmt&quot;

	&quot;golang.org/x/exp/maps&quot;
)

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:

&gt; go run .\main.go
# command-line-arguments
.\main.go:19:10: cannot use &amp;.autotmp_29 (type *struct { A int; B int }) as type *Key in argument to runtime.mapassign
&lt;autogenerated&gt;:1: cannot use &amp;.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.Equalmaps.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.

huangapple
  • 本文由 发表于 2022年4月7日 19:50:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/71781676.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定