Goのジェネリクスを手元で試す(2021/06現在)

## 概要

Goのジェネリクス[^generics]を試そうと思うと、以前は [go2go playground](https://go2goplay.golang.org/) で試すか、開発ブランチを自分でビルドする必要があったようですが、2021/06現在では比較的簡単に手元で試せるようになっています。


[^generics]: Go でジェネリクスを実現する機能は Type Parameter と呼ばれていますが、ここではジェネリクスと統一しました。

:::note info
下記では go1.17beta1 を使っていますが、 2021/07/13現在では go1.17rc1 がリリースされていますので、そちらをお使いください。。
:::

簡単に説明すると、次のような手順になります:

1. [公式ダウンロードサイト](https://golang.org/dl/) から go1.17beta1 のバイナリ配布物を取得し、インストールする
2. `export GOFLAGS="-gcflags=-G=3"` する
3. ジェネリクスの記述が入った任意のコードを `go build` する

Goのジェネリクスの書き方がよくわからなければ、[配布物に同梱の `$GOROOT/test/typeparam/` 配下](https://github.com/golang/go/tree/master/test/typeparam)に参考になるソースコードが多数置かれています。


### お断り

Go 1.71beta1 におけるジェネリクスはまだデフォルトで無効化された実験的なものです。正しいソースコードのはずなのにコンパイルできない、動作がおかしい等の不具合にあたる可能性もありますので、ご利用は自己責任でお願いします。


## 詳細

ここからは具体的に見ていきます。

### 手順1: go1.17beta1 のインストール

go1.17beta1 は [公式ダウンロードサイト](https://golang.org/dl/) の最下部のやや上、やや見つけにくいですが、 `go1.17beta1` と書かれたところにあります。(2021/06現在。すでにGA版の 1.17.x がリリースされているのであれば、そちらを使えばOK)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15320/6b64c955-113b-d999-30dd-453af2d639ef.png" width="30%">
(上記は公式サイトの該当箇所のスクリーンショット)

掲載されている sha256 値とダウンロードしたファイルのsha256値が一致することを確認します（一致しない場合はファイルが壊れています）。ここでは darwin 版バイナリ（非pkg）をダウンロードしたものとします。

```shell
$ sha256sum go1.17beta1.darwin-amd64.tar.gz
50046983ccd66180d1b6fbf39b4e5acb61dd08f1b53803661d86b60ba304bf80  go1.17beta1.darwin-amd64.tar.gz
```

続いてダウンロードしたアーカイブファイルを /usr/local/go に展開します。

```shell
$ sudo tar zxvf /path/to/go1.17beta1.darwin-amd64.tar.gz -C /usr/local
go/
go/api/
go/AUTHORS
go/bin/
go/codereview.cfg
go/CONTRIBUTING.md
go/CONTRIBUTORS
go/doc/
go/lib/
go/LICENSE
(以下略)
```

アーカイブは go というディレクトリのみを含んでいるので、  `-C /usr/local` をつければ /usr/local/go として展開されます。

最後に、`/usr/local/go/bin` にPATHを通します。
.bashrc 等、お使いの対話シェルの初期化ファイルに所定の記法で PATH を通します。
例えばbashの場合は次のような行を足せば良いです。

```bash
PATH=/usr/local/go/bin:$PATH
```

ターミナルやシェルを起動しなおすか、上記の行をコマンドラインとして実行すると、当該シェルのセッションで /usr/local/go/bin 配下のコマンドを実行できるようになります。


これで Go 1.17beta1 のインストールが完了しました。

### 手順2: `export GOFLAGS="-gcflags=-G=3"` する

`-gcflags=-G=3` はコンパイル時にGoのコンパイラに対してジェネリクスを有効にするよう指示するフラグです。
コマンドラインであれば `go build -gcflags=-G=3` と指定すればジェネリクスの文法を含んだGoソースコードをコンパイルできます。
そして、環境変数 GOFLAGS にこれを指定することで、コマンドラインでの指定を省略できます。

bash系であればこれはそのまま実行しても良いですし、継続的に有効にしたいのであれば .bashrc などに追記すると良いでしょう。


### 手順3: ジェネリクスの記述が入った任意のコードを `go build` する

例えば次のようなコードをコンパイルしてみましょう。

```go
package main

import (
	"fmt"
)

type Ordered interface {
	type int, float64
}

func min[T Ordered](x, y T) T {
	if x < y {
		return x
	}
	return y
}

func main() {
	a, b := 2, 3
	c, d := 2.5, 3.8
	fmt.Println(min(a, b))
	fmt.Println(min(c, d))
}
```

現行のGoでは明らかに通らないシンタックスを利用してジェネリックな min (2つの引数のうち小さいほうを返す) 関数が書かれています。

```shell
$ go build -o a.out && ./a.out
2
2.5
```

コンパイルと実行ができました。もちろん `go run main.go` などと run しても構いません。

本記事にしたがってGoをインストールした環境であれば、 `/usr/local/go/test/typeparam` 配下に様々なジェネリクスの利用例がありますので、色々と実験してみると理解が深まると思います。

## まとめ

* ジェネリクスが導入された Go 1.17beta1 はすでに簡単に利用可能だよ
* ジェネリクスを有効化してコンパイルするには `go build -gcflags=-G=3` を指定すればいいよ
* ジェネリクスのお手本は `/usr/local/go/test/typeparam` たくさんにあるよ


記事は以上になります。最後までお読みいただき、ありがとうございました。
誤り等ありましたらコメントや編集リクエストでお知らせください。