_content/doc: add a generics tutorial
This adds a tutorial introducing Go generics.
This change includes:
- A generics tutorial:
- _content/doc/tutorial/generics.md
- Updates to index topics so that they list the new content.
- _content/doc/index.html
- _content/doc/tutorial/index.html
Change-Id: I39abe0823c7d89dcb8ac89fcc1a2ab196a289614
Reviewed-on: https://go-review.googlesource.com/c/website/+/369614
Reviewed-by: Russ Cox <rsc@golang.org>
Trust: Michael Knyszek <mknyszek@google.com>
diff --git a/_content/doc/index.html b/_content/doc/index.html
index 03d95cb..a688cf7 100644
--- a/_content/doc/index.html
+++ b/_content/doc/index.html
@@ -43,6 +43,11 @@
Introduces the basics of writing a RESTful web service API with Go and the Gin Web Framework.
</p>
+<h3 id="generics-tutorial"><a href="/doc/tutorial/generics.html">Tutorial: Getting started with generics (beta)</a></h3>
+<p>
+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.
+</p>
+
<h3 id="writing-web-applications"><a href="/doc/articles/wiki/">Writing Web Applications</a></h3>
<p>
Building a simple web application.
diff --git a/_content/doc/tutorial/generics.md b/_content/doc/tutorial/generics.md
new file mode 100644
index 0000000..d6fe749
--- /dev/null
+++ b/_content/doc/tutorial/generics.md
@@ -0,0 +1,564 @@
+<!--{
+ "Title": "Tutorial: Getting started with generics"
+}-->
+
+> **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.
+
+In this tutorial, you'll declare two simple non-generic functions, then capture
+the same logic in a single generic function.
+
+You'll progress through the following sections:
+
+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](/doc/tutorial/index.html).
+
+**Note:** If you prefer, you can use
+[the Go playground in “Go dev branch” mode](https://go.dev/play/?v=gotip)
+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](#installing_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.
+
+<!-- TODO: Remove this section after release. -->
+#### Installing and using the beta {#installing_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 go1.18beta1=go
+ $ go version
+ ```
+Commands in this tutorial will assume you have aliased the beta name.
+
+## Create a folder for your code {#create_folder}
+
+To begin, create a folder for the code you’ll write.
+
+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.
+
+ Run the `go mod init` command, giving it your new code’s module path.
+
+ ```
+ $ go mod init example/generics
+ go: creating new go.mod: module example/generics
+ ```
+
+ **Note:** For production code, you’d specify a module path that’s more specific
+ to your own needs. For more, be sure to see
+ [Managing dependencies](/doc/modules/managing-dependencies).
+
+Next, you'll add some simple code to work with maps.
+
+## Add non-generic functions {#non_generic_functions}
+
+In this step, you'll add two functions that each add together the values of a
+map and return the total.
+
+You're declaring two functions instead of one because you're working with two
+different types of maps: one that stores `int64` values, and one that stores `float64` values.
+
+#### Write the code
+
+1. Using your text editor, create a file called main.go in the generics
+ directory. You’ll write your Go code in this file.
+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.
+ * Call the two functions you declared earlier to find the sum of each
+ map's values.
+ * Print the result.
+
+5. Near the top of main.go, just beneath the package declaration, import the
+ package you’ll need to support the code you’ve just written.
+
+ 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
+```
+
+With generics, you can write one function here instead of two. Next, you’ll
+add a single generic function for maps containing either integer or float values.
+
+## Add a generic function to handle multiple types {#add_generic_function}
+
+In this section, you'll add a single generic function that can receive a map
+containing either integer or float values, effectively replacing the two
+functions you just wrote with a single function.
+
+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.
+
+To support this, you'll write a function that declares _type parameters_ in
+addition to its ordinary function parameters. These type parameters make the
+function generic, enabling it to work with arguments of different types. You'll
+call the function with _type arguments_ and ordinary function arguments.
+
+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.
+
+While a type parameter's constraint typically represents a set of types, at
+compile time the type parameter stands for a single type – the type provided
+as a type argument by the calling code. If the type argument's type isn't
+allowed by the type parameter's constraint, the code won't compile.
+
+Keep in mind that a type parameter must support all the operations the generic
+code is performing on it. For example, if your function's code were to try to
+perform `string` operations (such as indexing) on a type parameter whose
+constraint included numeric types, the code wouldn't compile.
+
+In the code you're about to write, you'll use a constraint that allows either
+integer or float types.
+
+#### 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.
+ * Specify that the `m` argument is of type `map[K]V`, where `K` and `V`
+ are the types already specified for the type parameters. Note that we
+ know `map[K]V` is a valid map type because `K` is a comparable type. If
+ we hadn’t declared `K` comparable, the compiler would reject the
+ reference to `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.
+ * Specify type arguments – the type names in square brackets – to be
+ clear about the types that should replace type parameters in the
+ function you're calling.
+
+ As you'll see in the next section, you can often omit the type
+ arguments in the function call. Go can often infer them from your code.
+ * 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.
+
+In calling the generic function you wrote, you specified type arguments that
+told the compiler what types to use in place of the function's type parameters.
+As you'll see in the next section, in many cases you can omit these type
+arguments because the compiler can infer them.
+
+## Remove type arguments when calling the generic function {#remove_type_arguments}
+
+In this section, you'll add a modified version of the generic function call,
+making a small change to simplify the calling code. You'll remove the type
+arguments, which aren't needed in this case.
+
+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.
+
+Note that this isn't always possible. For example, if you needed to call a
+generic function that had no arguments, you would need to include the type
+arguments in the function call.
+
+#### 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 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
+```
+
+Next, you'll further simplify the function by capturing the union of integers
+and floats into a type constraint you can reuse, such as from other code.
+
+## Declare a type constraint {#declare_type_constraint}
+
+In this last section, you'll move the constraint you defined earlier into its
+own interface so that you can reuse it in multiple places. Declaring
+constraints in this way helps streamline code, such as when a constraint is
+more complex.
+
+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.
+
+Constraint interfaces can also refer to specific types, as you'll see in this
+section.
+
+#### Write the code
+
+1. Just above `main`, immediately after the import statements, paste the
+ following code to declare a type constraint.
+
+ ```
+ type Number interface {
+ int64 | float64
+ }
+ ```
+
+ 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.
+
+ Essentially, you're moving the union from the function declaration
+ into a new type constraint. That way, when you want to constrain a type
+ parameter to either `int64` or `float64`, you can use this `Number`
+ type constraint instead of writing out `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",
+ SumNumbers(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 {#conclusion}
+
+Nicely done! You've just introduced yourself to generics in 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](https://tour.golang.org/welcome/1) is a great step-by-step
+ introduction to Go fundamentals.
+* You'll find useful Go best practices described in
+ [Effective Go](/doc/effective_go) and
+ [How to write Go code](/doc/code).
+
+## Completed code {#completed_code}
+
+<!--TODO: Update text and link after release.-->
+You can run this program in the
+[Go playground](https://go.dev/play/p/apNmfVwogK0?v=gotip). 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
+}
diff --git a/_content/doc/tutorial/index.html b/_content/doc/tutorial/index.html
index c7eb82d..5a2a0ef 100644
--- a/_content/doc/tutorial/index.html
+++ b/_content/doc/tutorial/index.html
@@ -50,6 +50,15 @@
</tr>
<tr class="DocTable-row">
<td class="DocTable-cell">
+ <a href="/doc/tutorial/generics">Getting started with generics</a>
+ </td>
+ <td class="DocTable-cell">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.</td>
+ </tr>
+ <tr class="DocTable-row">
+ <td class="DocTable-cell">
<a href="/tour/">A Tour of Go</a>
</td>
<td class="DocTable-cell">