golang1.21泛型函数全面讲解

  目录

  Go 1.21中的泛型基本语法

  要定义泛型函数或类型,可以使用类型 关键字,后跟用方括号[]括起来的泛型形参的名称。例如,要创建一个接受任意类型的slice并返回其第一个元素的泛型函数,可以这样定义:

  func First[T any](items []T) T {

  return items[0]

  }

  在上面的例子中,表示类型参数,它表示任意类型。关键字表示T类型可以是任何有效类型。

  然后,可以使用任何切片类型调用函数,该函数将返回该切片的第一个元素。例如:

  func func1() {

  intSlice := []int{1, 2, 3, 4, 5}

  firstInt := First[int](intSlice) // returns 1

  println(firstInt)

  stringSlice := []string{"apple", "banana", "cherry"}

  firstString := First[string](stringSlice) // returns "apple"

  println(firstString)

  }

  func First[T any](items []T) T {

  return items[0]

  }

  注意,在调用泛型函数时,我们在方括号中指定类型参数。这允许编译器在编译期间为该类型生成特定的代码。

  我们还可以向泛型类型参数添加约束,将其限制为特定类型。例如,如果我们想将类型限制为仅实现接口的类型,可以使用如下约束:

  func PrintString[T Stringer](value T) {

  fmt.Println(value.String())

  }

  约束确保类型必须具有方法。这个约束允许我们在函数的参数上安全地调用方法。

  在Go 1.21中具有各种类型的泛型

  在另一个示例中,让我们编写函数,它对各种数字类型(如、、、、、和)执行加法操作。

  func SumGenerics[T int | int16 | int32 | int64 | int8 | float32 | float64](a, b T) T {

  return a + b

  }

  让我们看看如何利用这个泛型函数:

  func func2() {

  sumInt := SumGenerics[int](2, 3)

  sumFloat := SumGenerics[float32](2.5, 3.5)

  sumInt64 := SumGenerics[int64](10, 20)

  fmt.Println(sumInt) // returns 5

  fmt.Println(sumFloat) // returns 6.0

  fmt.Println(sumInt64) // returns 30

  }

  在上面的代码中,我们可以看到,通过在调用泛型函数时在方括号中指定类型参数,我们可以对不同的数字类型执行加法操作。类型约束确保只有指定的类型可以用作类型参数。

  以这种方式使用泛型使我们能够在不牺牲类型安全的情况下编写简洁且可重用的代码。可以使用各种数字类型调用该函数,编译器将为每种类型生成特定的代码,以确保正确执行加法操作。

  Go 1.21中具有任意数据类型的泛型

  泛型可以用于任意数据类型的序列化和反序列化,使用提供的序列化和反序列化函数:

  type Person struct {

  Name string

  Age int

  Address string

  }

  func Serialize[T any](data T) ([]byte, error) {

  buffer := bytes.Buffer{}

  encoder := gob.NewEncoder(&buffer)

  err := encoder.Encode(data)

  if err != nil {

  return nil, err

  }

  return buffer.Bytes(), nil

  }

  func Deserialize[T any](b []byte) (T, error) {

  buffer := bytes.Buffer{}

  buffer.Write(b)

  decoder := gob.NewDecoder(&buffer)

  var data T

  err := decoder.Decode(&data)

  if err != nil {

  return data, err

  }

  return data, nil

  }

  在本例中,我们有两个通用函数和,它们利用Go的包将任意数据类型转换为字节,反之亦然。

  func DeserializeUsage() {

  person := Person{

  Name: "John",

  Age: 30,

  Address: "123 Main St.",

  }

  serialized, err := Serialize(person)

  if err != nil {

  panic(err)

  }

  deserialized, err := Deserialize[Person](serialized)

  if err != nil {

  panic(err)

  }

  fmt.Printf("Name: %s, Age: %d, Address: %s", deserialized.Name, deserialized.Age, deserialized.Address)

  }

  Output:

  在上面的代码中,我们用一些数据创建了一个实例。然后使用函数将对象转换为字节数组。稍后,使用函数,将字节数组转换回对象。

  通过将序列化和反序列化函数定义为具有类型参数的泛型函数,我们可以序列化和反序列化任何支持使用包进行编码和解码的数据类型。

  在Go中使用泛型和Validate函数自定义验证器

  让我们用自定义验证器编写一个通用的函数。

  type Validator[T any] func(T) error

  func Validate[T any](data T, validators ...Validator[T]) error {

  for _, validator := range validators {

  err := validator(data)

  if err != nil {

  return err

  }

  }

  return nil

  }

  在本例中,我们有一个通用的函数,它使用自定义验证器执行数据验证。类型表示一个函数,它接受任意类型的值并返回一个错误。

  func StringNotEmpty(s string) error {

  if len(strings.TrimSpace(s)) == 0 {

  return fmt.Errorf("string cannot be empty")

  }

  return nil

  }

  func IntInRange(num int, min, max int) error {

  if num < min || num > max {

  return fmt.Errorf("number must be between %d and %d", min, max)

  }

  return nil

  }

  此外,我们有两个自定义验证器示例:和。

  确保字符串不为空,检查整数是否在指定范围内。

  package main

  func main() {

  person := Person{

  Name: "John",

  Age: 30,

  Address: "123 Main St.",

  }

  err := Validate(person, func(p Person) error {

  return StringNotEmpty(p.Name)

  }, func(p Person) error {

  return IntInRange(p.Age, 0, 120)

  })

  if err != nil {

  println(err.Error())

  panic(err)

  }

  println("Person is valid")

  }

  在本例中,我们创建了一个实例,并将其传递给函数。我们为结构定义了两个自定义验证器,检查字段是否为空,字段是否在有效范围内。如果任何验证器返回错误,验证过程将停止,并返回相应的错误。

  通过使用泛型和自定义验证器,函数允许跨不同数据类型进行灵活和可重用的数据验证,增强代码可重用性,并使添加或修改验证规则变得容易。

  让我们再写一个使用validator函数的例子

  type LoginForm struct {

  Username string

  Password string

  }

  func (f *LoginForm) Validate() error {

  return Validate(f,

  func(l *LoginForm) error {

  return StringNotEmpty(l.Username)

  },

  func(l *LoginForm) error {

  return StringNotEmpty(l.Password)

  },

  )

  }

  func ValidateUsage2() {

  loginForm := LoginForm{

  Username: "John",

  Password: "123",

  }

  err := loginForm.Validate()

  if err != nil {

  println(err.Error())

  panic(err)

  }

  println("Login form is valid")

  }

  在本例中,结构实现了一个方法,该方法利用了我们之前定义的泛型函数。

  方法调用通用的函数,并为它提供两个特定于类型的自定义验证器。验证器表示为闭包函数,使用验证器函数检查和字段是否为空。

  要验证实例,只需在实例本身上调用方法。

  package main

  func main() {

  loginForm := LoginForm{

  Username: "John",

  Password: "123",

  }

  err := loginForm.Validate()

  if err != nil {

  println(err.Error())

  panic(err)

  }

  println("Login form is valid")

  }

  如果任何验证器返回错误,验证过程将停止,并返回相应的错误。在这种情况下,我们相应地处理错误。

  总结

  这些示例展示了Go 1.21中泛型的强大功能和多功能性。泛型使我们能够编写可重用和类型安全的代码,这些代码可以处理不同的数据类型和结构,而不会牺牲代码的清晰度和可维护性。

  泛型为Go编程语言带来了显著的好处,增强了代码重用,减少了冗余,并改进了代码组织。有了泛型,开发人员就能够编写更有表现力、更简洁、更灵活的代码,以适应不同的数据类型和结构,从而为更具可扩展性和可维护性的软件开发铺平道路。

  以上就是golang1.21泛型函数全面讲解的详细内容,更多关于go1.21泛型函数的资料请关注脚本之家其它相关文章!

  您可能感兴趣的文章: