如何为可以使用len()函数的内容编写Go类型约束?

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

How to write a Go type constraint for something you can take len() of?

问题

我正在尝试为一个Go程序编写类型约束,该约束接受“任何你可以使用len()函数计算长度的东西”。但我真的搞不清楚该怎么做。

我想要的是这样的:

LenOf[m Measurable](m M) int {
    return len(m)
}

我尝试了几种方法。比如这个天真的方法,它可以编译通过,但不能在所有类型上正常工作(比如[]User):

type Measurable interface {
    ~string | []any | ~map[any]any
}

然后我尝试了下面这个方法,不仅使LenOf()函数的签名变得非常笨重,而且在调用处写起来也很笨拙(而且我仍然无法使其编译通过):

type Measurable[K comparable, V any] interface {
   ~string | []V | ~map[K]V
}
英文:

I am trying to write a type constraint for a Go program, that accepts "anything you can take len() of". But I can't really figure it out.

I want something like:

LenOf[m Measurable](m M) int {
    return len(m)
}

I tried a few things. Fx. this naiive thing, which do compile, but doesn't work on all types (like fx []User):

type Measurable interface {
    ~string | []any | ~map[any]any
}

Then went on to something like, the below which not only makes the function signature for LenOf() extremely clunky, but also have clumsy to write on call sites (and still can't get it to compile)

type Measurable[K comparable, V any] interface {
   ~string | []V | ~map[K]V
}

答案1

得分: 8

为什么?内置的 len 已经是“通用”的了。


话虽如此,让我们看看为什么定义这样一个约束是一个坏主意。Go 语言规范中有一段话 - 长度和容量,可以帮助我们:

如果参数类型是类型参数 P,调用 len(e)(或 cap(e)必须对 P 的类型集中的每个类型都有效。结果是与用 P 实例化时的类型参数对应的参数的长度(或容量)。

定义一个包含所有“可测量”类型的全面约束存在以下问题:

  • 它包括数组 [N]T,其中数组长度是类型的一部分,因此你的约束必须指定你想要捕获的所有可能的数组。
  • 它包括数组指针 *[N]T,你不能在类型约束中轻松地对其进行抽象。
  • 它包括映射,这迫使你捕获键 K 和值 V,它们可能与 T 相同也可能不同。此外,K 必须实现 comparable 接口。

因此,你需要编写类似于以下的代码:

type Measurable[T any, K comparable, V any] interface {
	~string | ~[]T | ~map[K]V | ~chan T
}

需要注意的是,这个代码并不包括数组,并且不能明确地捕获指针字面量,例如要匹配 []*int,你必须使用 *int 实例化 T

你可以简化掉 V

type Measurable[T any, K comparable] interface {
	~string | ~[]T | ~map[K]T | ~chan T
}

然后 LenOf 函数变成了:

func LenOf[T any, K comparable, M Measurable[T, K]](m M) int {
	return len(m)
}

但是你仍然需要提供 K,所以在调用时必须使用虚假类型来实例化 LenOf

LenOf[string, int]("foo")
//            ^ 实际上是无用的

而且你也不能利用参数的类型推断。

总之:只需使用 len。设计你的通用函数以使用支持长度的类型字面量,或者仅将那些你的函数可以合理处理的类型添加到约束中。

英文:

Why? The builtin len is already "generic".

<hr>

With that said, let's see why defining such a constraint is a bad idea. The Go spec has a paragraph — Length and capacity, that can help:

> If the argument type is a type parameter P, the call len(e) (or cap(e) respectively) must be valid for each type in P's type set. The result is the length (or capacity, respectively) of the argument whose type corresponds to the type argument with which P was instantiated.

The issues with writing an all-encompassing constraint for "measurable" types are:

  • it includes arrays [N]T, where the array length is part of the type, so your constraint would have to specify all possible arrays you want to capture
  • it includes array pointers *[N]T, which you can't easily abstract in a type constraint
  • it includes maps, which forces you to capture keys K and values V, which may or may not be the same as T. Plus, K must implement comparable.

So you'd have to write something like:

type Measurable[T any, K comparable, V any] interface {
	~string | ~[]T | ~map[K]V | ~chan T
}

which notably doesn't include arrays, and doesn't distinctly capture pointer literals, e.g. to match []*int, you would have to instantiate T with *int.

You might simplify V away:

type Measurable[T any, K comparable] interface {
	~string | ~[]T | ~map[K]T | ~chan T
}

The function LenOf then becomes:

func LenOf[T any, K comparable, M Measurable[T, K]](m M) int {
	return len(m)
}

but you still have to supply K, so you have to instantiate LenOf at call site with bogus types for the map key:

LenOf[string, int](&quot;foo&quot;)
//            ^ actually useless

and you can't take advantage of type inference with the argument either.

In conclusion: just use len. Design your generic functions to work with type literals that support length, or add to the constraint only those types that your functions are reasonably expected to handle.

huangapple
  • 本文由 发表于 2022年11月23日 04:39:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/74538708.html
匿名

发表评论

匿名网友

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

确定