Официальное руководство Golang 1.18: начало работы с дженериками

Go
Официальное руководство Golang 1.18: начало работы с дженериками

Официальное руководство Golang 1.18: начало работы с дженериками

Официальный адрес:golang.Google.can/doc/tutor IA…

Note: This is beta content.

This tutorial introduces the basics of generics in Go. With generics, you can declare and use functions or types that are written to work with any of a set of types provided by calling code.

В этом руководстве вы объявите две простые необобщенные функции, а затем зафиксируете ту же логику в одной универсальной функции. функция.

Вы пройдете через следующие разделы:

  1. Create a folder for your code.
  2. Add non-generic functions.
  3. Add a generic function to handle multiple types.
  4. Remove type arguments when calling the generic function.
  5. Declare a type constraint.

Note: For other tutorials, see Tutorials.

Note: If you prefer, you can useигровая площадка Go в режиме «Go dev branch» to edit and run your program instead.

Prerequisites

  • An installation of Go 1.18 Beta 1 or later. For installation instructions, see Installing and using the beta.

  • A tool to edit your code. Any text editor you have will work fine.

  • A command terminal. Go works well using any terminal on Linux and Mac, and on PowerShell or cmd in Windows.

Installing and using the beta

This tutorial requires the generics feature available in Beta 1. To install the beta, following these steps:

  1. Run the following command to install the beta.

    $ go install golang.org/dl/go1.18beta1@latest
    
  2. Run the following command to download updates.

    $ go1.18beta1 download
    
  3. Run go commands using the beta instead of a released version of Go (if you have one).

    You can run commands with the beta either by using the beta name or by aliasing the beta to another name.

    • Using the beta name, you can run commands by invoking go1.18beta1 instead of go:
      $ go1.18beta1 version
      
    • By aliasing the beta name to another name, you can simplify the command:
      $ alias go=go1.18beta1
      $ go version
      

Commands in this tutorial will assume you have aliased the beta name.

Create a folder for your code

Для начала создайте папку для кода, который вы будете писать.

  1. Open a command prompt and change to your home directory.

    On Linux or Mac:

     $ cd
    

    On Windows:

     C:\> cd %HOMEPATH%
    

    The rest of the tutorial will show a $ as the prompt. The commands you use will work on Windows too.

  2. From the command prompt, create a directory for your code called generics.

    $ mkdir generics
    $ cd generics
    
  3. Create a module to hold your code.

    Запустите команду инициализации go mod, указав путь к модулю вашего нового кода.

    $ go mod init example/generics
    go: creating new go.mod: module example/generics
    

    Note:Для производственного кода вы должны указать путь к модулю, который больше соответствует вашим собственным потребностям.Managing dependencies.

Далее вы добавите простой код для работы с картами.

Add non-generic functions

На этом шаге вы добавите две функции, каждая из которых суммирует значения карты и возвращает итог.

Вы объявляете две функции вместо одной, потому что работаете с двумя разными типами карт: одна хранит значения int64 и тот, в котором хранятся значения float64.

Write the code

  1. С помощью текстового редактора создайте файл с именем main.go в каталоге generics, в котором вы будете писать свой код Go. файл.

  2. Into main.go, at the top of the file, paste the following package declaration.

    package main
    

    A standalone program (as opposed to a library) is always in package main.

  3. Beneath the package declaration, paste the following two function declarations.

    // SumInts adds together the values of m.
    func SumInts(m map[string]int64) int64 {
        var s int64
        for _, v := range m {
            s += v
        }
        return s
    }
    
    // SumFloats adds together the values of m.
    func SumFloats(m map[string]float64) float64 {
        var s float64
        for _, v := range m {
            s += v
        }
        return s
    }
    

    In this code, you:

    • Declare two functions to add together the values of a map and return the sum.
      • SumFloats takes a map of string to float64 values.
      • SumInts takes a map of string to int64 values.
  4. At the top of main.go, beneath the package declaration, paste the following main function to initialize the two maps and use them as arguments when calling the functions you declared in the preceding step.

    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("Non-Generic Sums: %v and %v\n",
        SumInts(ints),
        SumFloats(floats))
    }
    

    In this code, you:

    • Initialize a map of float64 values and a map of int64 values, each with two entries.
    • Вызовите две объявленные ранее функции, чтобы найти сумму значений каждой карты.
    • Print the result.
  5. В верхней части main.go, прямо под объявлением пакета, импортируйте пакет, который вам понадобится для поддержки кода, который вы только что написали.

    The first lines of code should look like this:

    package main
    
    import "fmt"
    
  6. Save main.go.

Run the code

From the command line in the directory containing main.go, run the code.

$ go run . 
Non-Generic Sums: 46 and 62.97 

С помощью дженериков вы можете написать здесь одну функцию вместо двух.Затем вы добавите одну универсальную функцию для карт, содержащих либо целочисленные значения, либо значения с плавающей запятой.

Add a generic function to handle multiple types

В этом разделе вы добавите одну универсальную функцию, которая может получать карту, содержащую либо целые числа, либо значения с плавающей запятой, эффективно заменяя две функции, которые вы только что написали, одной функцией.

To support values of either type, that single function will need a way to declare what types it supports. Calling code, on the other hand, will need a way to specify whether it is calling with an integer or float map.

Чтобы поддержать это, вы напишете функцию, которая объявляет параметры типа в дополнение к своим обычным параметрам функции. Эти параметры типа делают функцию универсальной, позволяя ей работать с аргументами разных типов. Вы будете вызывать функцию с аргументами типа и обычные аргументы функции.

Each type parameter has a type constraint that acts as a kind of meta-type for the type parameter. Each type constraint specifies the permissible type arguments that calling code can use for the respective type parameter.

В то время как ограничение параметра типа обычно представляет собой набор типов, во время компиляции параметр типа представляет собой тип ingle — тип, предоставляемый в качестве аргумента типа вызывающим кодом.Если тип аргумента типа не разрешен ограничением параметра типа , код не будет компилироваться.

Имейте в виду, что параметр типа должен поддерживать все операции, которые выполняет над ним общий код.Например, если код вашей функции должен был попытаться выполнить строковые операции (такие как индексирование) над параметром типа, ограничение которого включало числовые типы , код не будет компилироваться.

В коде, который вы собираетесь написать, вы будете использовать ограничение, допускающее целочисленные типы или типы с плавающей запятой.

Write the code

  1. Beneath the two functions you added previously, paste the following generic function.

    // SumIntsOrFloats sums the values of map m. It supports both int64 and float64
    // as types for map values.
    func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
        var s V
        for _, v := range m {
            s += v
        }
        return s
    }
    

    In this code, you:

    • Declare a SumIntsOrFloats function with two type parameters (inside the square brackets), K and V, and one argument that uses the type parameters, m of type map[K]V. The function returns a value of type V.
    • Specify for the K type parameter the type constraint comparable. Intended specifically for cases like these, the comparable constraint is predeclared in Go. It allows any type whose values may be used as an operand of the comparison operators == and !=. Go requires that map keys be comparable. So declaring K as comparable is necessary so you can use K as the key in the map variable. It also ensures that calling code uses an allowable type for map keys.
    • Specify for the V type parameter a constraint that is a union of two types: int64 and float64. Using | specifies a union of the two types, meaning that this constraint allows either type. Either type will be permitted by the compiler as an argument in the calling code.
    • Укажите, что аргумент m имеет тип map[K]V, где K и V — типы, уже указанные для параметров типа.Обратите внимание, что мы знаем map[K]V является допустимым типом карты, потому что K является сравнимым типом.Если бы мы не объявили сравнимый тип K, компилятор отклонить ссылку на map[K]V.
  2. In main.go, beneath the code you already have, paste the following code.

    fmt.Printf("Generic Sums: %v and %v\n",SumIntsOrFloats[string, int64](ints),SumIntsOrFloats[string, float64](floats))
    

    In this code, you:

    • Call the generic function you just declared, passing each of the maps you created.
    • Укажите аргументы типа — имена типов в квадратных скобках — чтобы было ясно, какие типы следует заменить type. параметры в функции, которую вы вызываете.
    • Как вы увидите в следующем разделе, вы можете часто опускать аргументы типа в вызове функции, Go часто может вывести их. из вашего кода.
    • Print the sums returned by the function.

Run the code

From the command line in the directory containing main.go, run the code.

$ go run .
Non-Generic Sums: 46 and 62.97
Generic Sums: 46 and 62.97

To run your code, in each call the compiler replaced the type parameters with the concrete types specified in that call.

При вызове написанной вами универсальной функции вы указали аргументы типа, которые сообщали компилятору, какие типы использовать вместо параметров типа функции.Как вы увидите в следующем разделе, во многих случаях вы можете опустить эти аргументы типа, потому что компилятор может вывести их.

Remove type arguments when calling the generic function

В этом разделе вы добавите модифицированную версию универсального вызова функции, внеся небольшое изменение для упрощения вызывающего кода.Вы удалите аргументы типа, которые в данном случае не нужны.

You can omit type arguments in calling code when the Go compiler can infer the types you want to use. The compiler infers type arguments from the types of function arguments.

Обратите внимание, что это не всегда возможно.Например, если вам нужно вызвать универсальную функцию без аргументов, вам нужно будет включить аргументы типа в вызов функции.

Write the code

  • In main.go, beneath the code you already have, paste the following code.

    fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",SumIntsOrFloats(ints),SumIntsOrFloats(floats))
    

    In this code, you:

    • Call the generic function, omitting the type arguments.

Run the code

From the command line in the directory containing

$ go run .
Non-Generic Sums: 46 and 62.97
Generic Sums: 46 and 62.97
Generic Sums, type parameters inferred: 46 and 62.97

Далее вы еще больше упростите функцию, захватив объединение целых чисел и чисел с плавающей запятой в ограничение типа, которое вы можете повторно использовать, например, из другого кода.

Declare a type constraint

В этом последнем разделе вы переместите ограничение, которое вы определили ранее, в его собственный интерфейс, чтобы вы могли повторно использовать его в нескольких местах. Объявление ограничений таким образом помогает упростить код, например, когда ограничение более сложное.

You declare a type constraint as an interface. The constraint allows any type implementing the interface. For example, if you declare a type constraint interface with three methods, then use it with a type parameter in a generic function, type arguments used to call the function must have all of those methods.

Интерфейсы ограничений также могут ссылаться на определенные типы, как вы увидите в этом разделе.

Write the code

  1. Just above main, immediately after the import statements, paste the following code to declare a type constraint.

    $ go run .
    Non-Generic Sums: 46 and 62.97
    Generic Sums: 46 and 62.97
    Generic Sums, type parameters inferred: 46 and 62.97
    

    In this code, you:

    • Declare the Number interface type to use as a type constraint.

    • Declare a union of int64 and float64 inside the interface.

      По сути, вы перемещаете объединение из объявления функции в новое ограничение типа.Таким образом, когда вы хотите ограничить параметр типа значением int64 или float64, вы можете использовать это ограничение типа Number вместо записи int64 | float64.

  2. Beneath the functions you already have, paste the following generic SumNumbers function.

    // SumNumbers sums the values of map m. Its supports both integers
    // and floats as map values.
    func SumNumbers[K comparable, V Number](m map[K]V) V {
        var s V
        for _, v := range m {
            s += v
        }
        return s
    }
    

    In this code, you:

    • Declare a generic function with the same logic as the generic function you declared previously, but with the new interface type instead of the union as the type constraint. As before, you use the type parameters for the argument and return types.
  3. In main.go, beneath the code you already have, paste the following code.

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

In this code, you:

  • Call SumNumbers with each map, printing the sum from the values of each.

    As in the preceding section, you omit the type arguments (the type names in square brackets) in calls to the generic function. The Go compiler can infer the type argument from other arguments.

Run the code

From the command line in the directory containing main.go, run the code.

$ go run .
Non-Generic Sums: 46 and 62.97
Generic Sums: 46 and 62.97
Generic Sums, type parameters inferred: 46 and 62.97
Generic Sums with Constraint: 46 and 62.97

Conclusion

Отлично сделано! Вы только что познакомились с дженериками в Go.

If you want to keep experimenting, you can try writing the Number interface in terms of constraints.Integer and constraints.Float, to allow more numeric types.

Suggested next topics:

  • The Go Tour is a great step-by-step introduction to Go fundamentals.
  • Вы найдете полезные рекомендации по Go, описанные вEffective Go and How to write Go code.

Completed code

You can run this program in the Go playground. On the playground simply click the Run button.

package main

import "fmt"

type Number interface {
    int64 | float64
}

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("Non-Generic Sums: %v and %v\n",SumInts(ints),SumFloats(floats))

    fmt.Printf("Generic Sums: %v and %v\n",SumIntsOrFloats[string, int64](ints),SumIntsOrFloats[string, float64](floats))

    fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",SumIntsOrFloats(ints),SumIntsOrFloats(floats))

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

// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
    var s int64
    for _, v := range m {
        s += v
    }
    return s
}

// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
    var s float64
    for _, v := range m {
        s += v
    }
    return s
}

// SumIntsOrFloats sums the values of map m. It supports both floats and integers
// as map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

// SumNumbers sums the values of map m. Its supports both integers
// and floats as map values.
func SumNumbers[K comparable, V Number](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}