英文:
What is the difference between comparable and any?
问题
我尝试过在Go中使用泛型,但我不太理解何时使用any
或comparable
作为类型参数。有人可以帮助我理解这些吗?
英文:
I have tried to use generics with Go, but I don't really understand when we use any
or comparable
as type parameter. Can someone help to understand these?
答案1
得分: 10
这取决于你想如何使用参数类型的值。约束条件限制了你对这些类型的值可以做什么操作。
any
是 interface{}
的别名,它允许任何类型。如果一个参数可以是任意类型,那么基本上你不能对它做任何操作,因为你无法保证它的具体类型。
comparable
约束只允许可比较的类型,也就是说,可以使用 ==
和 !=
运算符来比较它们的值。如果你想将该类型用作映射的键(映射要求键的类型必须是可比较的),或者如果你想在切片中查找元素,并且想使用 ==
运算符将元素与某个值进行比较,那么使用 comparable
约束是很有用的。
举个例子,我们来编写一个通用的获取映射值的函数:
func get[K comparable, V any](m map[K]V, key K) V {
return m[key]
}
这里的键类型 K
必须是可比较的,否则它不能作为某个映射的键类型(例如 m[K]V
)。而值类型 V
则不需要受到约束,它可以是任何类型,我们并不对它做任何操作(只是返回一个类型为 V
的值),所以在这里使用 any
是最好的选择。
再举个例子,一个查找切片中元素的函数:
func find[V comparable](what V, s []V) int {
for i, v := range s {
if v == what {
return i
}
}
return -1
}
find()
函数返回切片 s
中第一个出现的 what
的索引,如果它不在切片中,则返回 -1
。这里的类型参数 V
必须是可比较的,否则你无法写 v == what
,使用 V any
会导致编译时错误。约束条件 comparable
确保了 find()
函数只能实例化为具有定义和允许使用 ==
运算符的类型(并使用值调用)。
英文:
It depends on what / how you want to use values of the parameter type. Constraints restrict what you can do with values of those types.
any
is an alias for interface{}
which allows any type. If a parameter can be of any type, that basically won't allow you to do anything with it because you have no guarantee what it will be.
The comparable
constraints only allows types that are comparable, that is, the ==
and !=
operators are allowed to use on values of them. This is good if you want to use the type as a key in a map (maps require key types to be comparable), or if you you want to find an element in a slice, and you want to use the ==
operator to compare the elements to something.
As an example, let's write a generic map-get function:
func get[K comparable, V any](m map[K]V, key K) V {
return m[key]
}
The K
key type must be comparable
, else it cannot be used as the key type of some map (m[K]V
in the example). V
on the other hand shouldn't be constrained, the value type may be anything, and we're not doing anything with it (just returning a value of type V
), so using any
here is the best choice.
Another example, a slice-find function:
func find[V comparable](what V, s []V) int {
for i, v := range s {
if v == what {
return i
}
}
return -1
}
find()
returns the index of the first occurrence of what
in the slice s
, and if it's not in the slice, returns -1
. The type parameter V
here must be comparable
, else you couldn't write v == what
, using V any
would be a compile-time error. The constraint comparable
ensures this find()
function can only be instantiated with types (and called with values) where the ==
operator is defined and allowed.
答案2
得分: 0
comparable
和any
之间的区别将在Go 1.20(2023年第一季度)中发生变化,并且已经接受了提案"56548: spec: allow basic interface types to instantiate comparable type parameters"。
any
将实现可比较约束(在Go 1.20之前它没有实现)。
Go 1.20-rc1中提到:
> 可比较类型(如普通接口)现在可以满足可比较约束,即使类型参数不是严格可比较的(比较可能在运行时引发恐慌)。
>
> 这使得可以使用非严格可比较的类型参数(例如用户定义的泛型映射键的类型参数)来实例化一个受可比较约束的类型参数,例如接口类型或包含接口类型的复合类型。
原则是:
> 在替换之后,每个类型参数必须满足相应类型参数的约束(必要时进行实例化)。否则,实例化失败。
其中“满足”是指:
> 类型T
满足约束接口C
,如果
>
> - T
实现了C
;或者
> - C
可以写成interface{ comparable; E }
的形式,其中E
是基本接口,T
是可比较的并且实现了E
。
例如:
> 目前,any
不实现可比较约束。
>
> 在建议的更改中,any
将被允许作为可比较的类型参数:comparable
可以写成interface{ comparable; E }
,因此新规则适用,并且any
是规范可比较的并且实现了E
(在这种情况下,E
是空接口)。
>
> 目前,在类型参数列表中的类型参数P
>
> [P interface{ comparable; fmt.Stringer }]
>
>无法使用类型S
进行实例化
>
>
>go >type S struct { > data any >} > >func (S) String() string >
>
>因为S
不是严格可比较的。
>在建议的更改中,S
只需要是规范可比较的(它是)并且实现fmt.Stringer
(它实现了)。
(“规范可比较”适用于可比较操作数的类型)
(与“严格可比较”相对应,后者适用于comparable
中的类型,即==
和!=
被定义并且保证不会引发恐慌的(非接口)类型集合)
已开始的实现:
-
CL 453979: “
cmd/compile
: enable new comparable semantics by default” -
CL 453978: “
go/types
,types2
: make the new comparable semantics the default”
英文:
The difference between comparable
and any
will change with Go 1.20 (Q1 2023) and (accepted) the proposal "56548: spec: allow basic interface types to instantiate comparable type parameters".
any
will implement the comparable constraint (which it does not before Go 1.20).
Go 1.20-rc1 states:
> Comparable types (such as ordinary interfaces) may now satisfy comparable constraints, even if the type arguments are not strictly comparable (comparison may panic at runtime).
>
> This makes it possible to instantiate a type parameter constrained by comparable (e.g., a type parameter for a user-defined generic map key) with a non-strictly comparable type argument such as an interface type, or a composite type containing an interface type.
The principle is:
> After substitution, each type argument must satisfy the constraint (instantiated, if necessary) of the corresponding type parameter. Otherwise instantiation fails.
With "satisfy" being:
> A type T
satisfies a constraint interface C
if
>
> - T
implements C
; or
> - C
can be written in the form interface{ comparable; E }
, where E
is a basic interface and T
is comparable and implements E
.
Example:
> Currently, any
does not implement the comparable constraint.
>
> With the proposed change any
will be permitted as type argument for comparable: comparable
can be written as interface{ comparable; E }
and thus the new rule applies, and any
is spec-comparable and implements E
(where E
is the empty interface in this case).
>
>Currently, the type parameter P
in the type parameter list
>
> [P interface{ comparable; fmt.Stringer }]
>
>cannot be instantiated with the type S
>
>
>go
>type S struct {
> data any
>}
>
>func (S) String() string
>
>
> because S
is not strictly comparable.
> With the proposed change, S
must only be spec-comparable (which it is) and implement fmt.Stringer
(which it does).
("spec-comparable" are for types of comparable operands)
(as opposed to "strictly comparable", which is for the types in comparable
, namely the set of (non-interface) types for which ==
and !=
are defined and for which these operations are guaranteed to not panic)
The implementation as started:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论