[T any]
上其实没有任何约束,其效果等同于interface{}
。虽然T
可以是任何类型,但是由于any
不能反应出任何特征,所以在其上的操作非常受限,比如不能进行如下操作:
gofunc Add[T any](x, y T) T {
return x + y
// ^
// invalid operation: operator + not defined on x (variable of type T constrained by any)
}
在 Go 中定义约束就是定义接口。下面的例子使用了标准库fmt.Stringer
接口约束类型参数。
go// packge fmt
type Stringer interface {
String() string
}
// 约束
func Stringify[T fmt.Stringer](s T) string {
return s.String()
}
在泛型出来以前,interface 中只能包含方法。泛型出来后 interface 被用于约束类型参数,并且其不仅可以包含方法,还可以包含其他类型,下面的例子展示了 interface 包含 int 类型:
gotype OnlyInt interface {
int
}
// 使用
func Double[T OnlyInt](v T) T {
return v * 2
}
通过类型联合来扩大约束范围,可以看成or
操作,可以包含 interface 或者其他约束。
gotype Integer interface {
int | int8 | int16 | int32 | int64
}
type Float interface {
float32 | float64
}
type Complex interface {
complex64 | complex128
}
type Number interface {
Integer | Float | Complex
}
满足某个约束的所有类型的集合。
在约束中使用匿名 struct:
gotype Pointish interface {
struct{ X, Y int }
}
注意,虽然约束中明确指出 struct 包含 X,Y,但是不能在函数中使用,参考
gofunc GetX[T Pointish](p T) int {
return p.X
// ^
// p.X undefined (type T has no field or method X)
}
就目前来说,所有的接口都可以作为约束,但是约束不能当接口用,比如函数参数:
gofunc Double(p Number) Number {
// ^ ^
// cannot use type Number outside a type constraint: interface contains type constraints
}
gotype Cow struct{ moo string }
type Chicken struct{ cluck string }
type Animal interface {
Cow | Chicken
}
type Farm[T Animal] []T
func Foo() {
_ = Farm[Cow]{}
_ = Farm[Chicken]{}
_ = Farm[Animal]{}
// ^
// cannot use type Animal outside a type constraint: interface contains type constraints
}
报错的原因就是约束并不是类型,无法用于泛型实例化。
gotype Integer interface {
int | int8 | int16 | int32 | int64
}
type MyInt int
func Double[T Integer](v T) T {
return v * 2
}
func Foo() {
fmt.Println(Double(MyInt(1)))
// ^
// MyInt does not satisfy Integer (possibly missing ~ for int in Integer)
}
上面的代码无法编译通过,由于通过类型定义,MyInt 不是 int,所以对于有相同底层类型的类型来说,无法传递约束中未出现的具有相同底层类型的值。
想要解决上面的问题可以使用~
符号,其含义是包含当前类型及其具有相同底层类型的类型。
gotype ApproximatelyInt interface {
~int
}
类型近似也可以用在复合类型字面量上:
gotype Pointish interface {
~struct{ x, y int }
}
在定义接口时每行为一个方法,其含义是,若要实现该接口则需要某类型实现该接口中的所有方法。对于约束来说,如果某类型想要满足一个约束,那么需要其满足该约束的所有要求。
对于下面的约束,没有任何类型能满足,因为不可能有一个类型同时是int && string
。
gotype Unpossible interface {
int
string
}
想要解决这个问题可以使用类型近似:
gotype IntStringer interface {
~int
fmt.Stringer
}
官方的实验性包 constraints 包含了一些有用的约束:
gopackage product
import "golang.org/x/exp/constraints"
type Number interface {
constraints.Integer | constraints.Float | constraints.Complex
}
func Product[T Number](x, y T) T {
return x * y
}
package product_test
import (
"product"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
func TestProductOfInts2And3Is6(t *testing.T) {
t.Parallel()
want := 6
got := product.Product(2, 3)
if !cmp.Equal(want, got) {
t.Error(cmp.Diff(want, got))
}
}
可以使用>
、<
、>=
、<=
gopackage main
import (
"fmt"
"golang.org/x/exp/constraints"
)
func main() {
fmt.Println(Greater("a", "b"))
}
func Greater[T constraints.Ordered](x, y T) T {
if x > y {
return x
}
return y
}
/*
packge contraints
type Ordered interface {
Integer | Float | ~string
}
*/
gofunc Identity[T, U any](x T, y U) (T, U) {
return x, y
}
有些类型不能大小比较(ordered),但是可以相等比较(comparable)。
由于很多类型无法预定义到约束中(比如用户自定义的 struct),所以 constraints 包中并没有 comparable 约束,而是直接把 comparable 内置为关键字,利用编译器进行检查。
gofunc Equal[T comparable](x, y T) bool {
return x == y
}
gopackage main
import (
"fmt"
"golang.org/x/exp/constraints"
)
func Max[E constraints.Ordered](s []E) E {
var max E
for _, e := range s {
if e > max {
max = e
}
}
return max
}
func main() {
s := []string{"faith", "hope", "love"}
fmt.Println(Max(s))
}
上面的 Max 在其 body 中使用了类型参数 E,这个 E 被称为抽象类型。有时想返回其零值,可以使用如下方法:
govar max E
return max
// 如果约束允许也可以使用这个方法
return E(0)
一些例子:
go// 空约束
func Identity[T interface{}](v T) T {}
// 包含一个方法
func Stringify[T interface{ String() string }](s T) string {
return s.String()
}
当约束中只有一个类型元素时,可以省略interface{...}
gofunc Increment[T ~int](v T) T {
return v + 1
}
// 下面的是错误示例
func Increment[T ~int; ~float64](v T) T {
// syntax error: unexpected semicolon, expecting comma or ]
func Increment[T String() string](v T) T {
// syntax error: unexpected (, expecting comma or ]
type Intish ~int
// syntax error: unexpected ~ in type declaration
gofunc Contains[T interface{ Equal(T) bool }](s []T, v T) bool {}
type Equaler interface {
Equal(Equaler) bool
}
本文作者:jdxj
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!