New features of experimental Go generics

Posted by dillonit on Sun, 06 Feb 2022 03:54:04 +0100

New features of experimental Go generics:

I Prerequisite: run the following two lines of commands to download the go compilation environment that supports running generics.

go get golang.org/dl/go1.18beta1@latest
// Download go1 18beta1 executable file to the bin directory of go configuration
go1.18beta1 download
// Check whether the download is successful
go1.18beta1 version
//The test code should use go1 18beta1 to run.

II Do not use generic implementation to sum all values in a map, and the value may be int64 or float64.
Code example:

package main


/**
   The following example shows two different Sum type methods for handling accumulation
 */


import "fmt"


// SumInts accumulates the int64 type value in the map
func SumInts(m map[string]int64) int64 {
   var s int64
   for _, v := range m {
      s += v
   }
   return s
}

// SumFloats accumulates the value of float64 type in the map
func SumFloats(m map[string]float64) float64 {
   var s float64
   for _, v := range m {
      s += v
   }
   return s
}

func main() {
   // Initialize the map for testing int64 type accumulation
   ints := map[string]int64{
      "first": 34,
      "second": 12,
   }

   // Initialize the map of test float64 type accumulation
   floats := map[string]float64{
      "first": 35.98,
      "second": 26.99,
   }

   fmt.Printf("Non-Generic Sums: %v and %v\n",
      SumInts(ints),
      SumFloats(floats))

}

NOTE: you can use the command just downloaded or go run directly:

III Goal: use generic functions to handle multiple types.
The following example replaces the above two methods to handle sumxxx function with a generic map that can receive both integer and float at the same time.
To achieve this goal, we need to clarify the following steps:

  • Define generic types to support multiple types at the same time.
  • When writing a function, in addition to the original function definition, you also need to define the type of type parameters generics. This definition allows the method to use the characteristics of generics so that it can receive different types of parameters at the same time.
  • Type parameters can limit a group of types, but they can only represent one type when compiling. This type is the type passed in by calling this method. If the type passed in does not meet the restrictions of type parameters, it cannot be compiled.

How are the type parameters defined?
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V

When the function we define needs to use generics, we need to use [] to define the type parameters used between the function name and the actual parameter list. If multiple types are supported, separate them with |.

Code example:

package main


import "fmt"


func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
   var result V
   for _,v := range m {
      result += v
   }

   return result
}

func main() {

   // Initialize a map for the integer values
   ints := map[string]int64{
      "first": 34,
      "second": 12,
   }

   // Initialize a map for the float values
   floats := map[string]float64{
      "first": 35.98,
      "second": 26.99,
   }

   fmt.Printf("Generic Sums: %v and %v\n",
      SumIntsOrFloats[string, int64](ints),
      SumIntsOrFloats[string, float64](floats))
}
Note: If the code is in goland Written in, pay attention goland The compilation environment of will prompt an error, which should be run here commandline Use in   go1.18beta1 run main.go  To run.
   As you can see from the example, not only the function definition needs to be added[K Ktype, V Vtype]When calling a method, you should also give K V Restrict the type of incoming.
In fact, it's OK to call a method without passing a type declaration. The compiler will have the function of type inference fmt You can also call it in the following way.
fmt.Printf("Generic Sums: %v and %v\n",
   SumIntsOrFloats(ints),
   SumIntsOrFloats(floats))

IV Generic parameters use interface declarations:
The type of V here can be multiple types of parameters. We may need to support different types of sum operations. It is inconvenient to define each method in this way. The following is an example of using the interface to declare the types supported by generic parameters:

type Number interface {
   int64 | float64
}

Define an interface type of type Number, and define all supported parameters here.

Function declaration modification:

func SumNumbers[K comparable, V Number](m map[K]V) V {
   var result V
   for _,v := range m {
      result += v
      }
      
   return result
   }

Here, the type of V is changed to the type of interface.

There is also no problem calling method printing:

fmt.Printf("Generic Sums with Constraint: %v and %v\n",
   SumNumbers(ints),
   SumNumbers(floats))

Complete code example:
https://github.com/hinss/golearn/tree/master/generic_test

Topics: Go Back-end