Slide 1

Slide 1 text

The Go gopher was designed by Renée French. The gopher stickers was made by Takuya Ueda. Licensed under the Creative Commons 3.0 Attributions license. Go1.18最新情報 ジェネリクス、Fuzzing、Workspaceモード 2022/01/15(土) tenntenn Conference 2022 資料:https://tenn.in/go118 動画:https://tenn.in/go118-video

Slide 2

Slide 2 text

上田拓也 Go ビギナーズ
 Go Conference
 @tenntenn tenntenn.dev Google Developer Expert (Go) 一般社団法人 Gophers Japan 代表理事 Experts Team

Slide 3

Slide 3 text

この活動を支える企業 ■ 株式会社トレタ ● https://corp.toreta.in/ ● https://corp.toreta.in/recruit/engineer/

Slide 4

Slide 4 text

Go1.18 リリースパーティ ■ 詳細はこちらのイベントで! ● 2022年02月18日(金) 19:00 〜 ● https://gocon.connpass.com/event/234198/ ● Remoを使って開催予定! ○ YouTube Liveでの配信はありません ○ アーカイブは残す予定です

Slide 5

Slide 5 text

Goの開発サイクル ■ Goは毎年8月と2月にリリースされる ● マイナーバージョンがアップされる ● リリース3ヶ月前からDeveloper Freezeになる ○ バグ修正とドキュメント修正以外は基本的に行われない ● リリース2ヶ月前からベータ版がリリースされる ● リリース1ヶ月前からRC版がリリースされる 引用元:Go Release Cycle 参考:Goのリリースプロセスとブランチ戦略 - YAMAGUCHI::weblog

Slide 6

Slide 6 text

Go1の後方互換 ■ Go1の間は言語の後方互換が保たれる ● https://go.dev/doc/go1compat ● 言語仕様に破壊的変更が加わらない ● 標準ライブラリも後方互換が保たれる ● 一部例外はある ○ セキュリティ、重大なバグなど ● Go1.0で書いたコードはGo1.16でもビルドできる ○ 場合によってはgo fixやgo fmtを掛ける必要がある ● Go1を対象に執筆された書籍やブログは言語仕様に関しては 互換が保たれているためGo1.xの間は有効である ○ ランタイムやgo toolに関しては変更される可能性あり

Slide 7

Slide 7 text

Goの開発プロセスとGo2 ■ Goの開発プロセス ● 実際にGoを使った中で問題を見つける ● 解決策をコミュニティに提案し議論する ● 実装して評価する ● 問題なければリリースされる ■ Go2とは? ● https://blog.golang.org/toward-go2 ● 明確なターゲットがあるわけではない ● Go1.xとして開発されていき、Go1の後方互換が 保てなくなるような変更を加える必要になったら Go2としてリリースされる Goの開発プロセス

Slide 8

Slide 8 text

Go1.18で入る予定の機能 ■ Go 1.18で入る予定の機能 ● Two New Tutorials for 1.18 - The Go Blog ● 型パラメタ(ジェネリクス) ○ 型セット ● Fuzzing ● Workspace mode ■ Go 1.19+で入る予定の機能 ● 標準パッケージでの型パラメタの利用 ○ golang.org/x/expでmapsパッケージとslicesパッケージは開発中 ● 脆弱DB(未承認) ○ pkg.go.devでexperiment=vulnsフラグをつけると出る

Slide 9

Slide 9 text

Go1.18 Beta1を試す ■ go1.18beta1コマンドをインストールする ● 公式で複数のバージョンをインストールできる方法を提供している # Go1.18 Beta1をインストールする $ go install golang.org/dl/go1.18beta1@latest $ go1.18beta1 download # Go1.16.1をインストールする $ go install golang.org/dl/go1.16.1@latest $ go1.16.1 download # dev.fuzzブランチをビルドしてインストールする $ go install golang.org/dl/gotip@latest $ gotip download dev.fuzz # ブランチ名は省略可 ※ Go1.15以前は go getコマンドを用いる

Slide 10

Slide 10 text

The Go Playgroundで試す ■ 開発版を試すことができる ● 最新のPlaygroundは実行する環境を選べる ● シェアURLに?v=gotipをつけると開発版で開く ○ 例:https://go.dev/play/p/kg938K7hORf?v=gotip

Slide 11

Slide 11 text

gpコマンドを使う ■ github.com/tenntenn/goplayground ● The Go PlaygroundのAPI クライアント ● gpというコマンドも付属している ○ -backendというオプションがある # インストール $ go install github.com/tenntenn/goplayground/cmd/gp@latest # 実行 $ gp run -backend gotip main.go # フォーマット $ gp format -backend gotip main.go # シェアURLの生成 $ gp share -backend gotip main.go ダウンロード $ gp dl -backend gotip https://go.dev/play/p/SnqdM4Dvvjt?v=gotip

Slide 12

Slide 12 text

型パラメタ(ジェネリクス)

Slide 13

Slide 13 text

型パラメタを持つ関数の定義 ■ 関数名の後ろに型パラメータを指定する ● 関数呼び出しで型引数を指定する(インスタンス化) ● 実際は型パラメタTは型引数で指定した型として扱われる ● anyは任意の型を指定できることを表す(詳細は後述) ● パッケージ関数のみ func Print[T any](s []T) { for _, v := range s { fmt.Print(v) } } func main() { Print[string]([]string{"Hello, ", "playground\n"}) Print[int]([]int{10, 20}) } Playgroundで動かす 型パラメタ 13 型引数

Slide 14

Slide 14 text

型パラメタを持つ関数のインスタンス化 ■ 型引数を指定することでインスタンス化できる ● 変数への代入なども行える ● インスタンス化しないと呼び出しなどはできない 14 var printStr func([]string) = Print[string] printStr([]string{"Hello, ", "playground\n"}) Playgroundで動かす Print[T] Print[string] func([]string) Print[int] func([]int) T => string T => int インスタンス化

Slide 15

Slide 15 text

型推論 ■ 型推論により型引数を省略することが可能 ● 型推論は関数呼び出しのみ行われる ● 型統一アルゴリズムを用いる(2パスアルゴリズム) ○ 1: 型なしの定数を無視して統一化する ○ 2: 型なしの定数をそのデフォルトの型とみなして扱う func main() { Print([]string{"Hello, ", "playground\n"}) Print([]int{1, 2, 3}) } Playgroundで動かす

Slide 16

Slide 16 text

複数の型パラメタ ■ 型パラメタは複数持つことができる ● 型パラメタが異なっていても同じ型引数を指定可能 ● 型パラメタを組み合わせて1つの型を表現することも可能 func Tuple[X, Y any](x X, y Y) struct {X X; Y Y} { return struct {X X; Y Y}{x, y} } func main() { t := Tuple(10, "hoge") fmt.Println(t.X, t.Y) } Playgroundで動かす

Slide 17

Slide 17 text

型パラメタを持つ型 ■ 型定義でも型パラメタを使用できる ● 型名の後ろに型パラメタを指定する ● 型エイリアスを用いてもインスタンス化できる // Vector型は型パラメタとしてTを持つ type Vector[T any] []T // 型引数を指定してインスタンス化する var ns Vector[int] = Vector[int]{10, 20, 30} // 型エイリアスを用いたインスタンス化 type IntVector = Vector[int] ms := IntVector{100, 200, 300} Playgroundで動かす

Slide 18

Slide 18 text

型パラメタを持つ型のメソッド ■ 型パラメタを持つ型にメソッドを設けることは可能 ● メソッドで新たに型パラメタを増やすことはできない 18 type Vector[T any] []T func (v *Vector[T]) Push(x T) { *v = append(*v, x) } func main() { var ns Vector[int] = Vector[int]{10, 20, 30} ns.Push(40) fmt.Println(ns) // [10 20 30 40] } Playgroundで動かす

Slide 19

Slide 19 text

再帰的な型定義 ■ 型の要素(フィールドなど)に型自身を使用できる ● 型リテラルで用いるのはOK ○ ポインタ、スライスなど ● 型パラメタを再帰的に使うのはNG 19 type List[T any] struct { Next *List[T] Value T } Playgroundで動かす

Slide 20

Slide 20 text

型パラメタへの制約 ■ インタフェースによって制約を加える ● anyは任意の型を許可する ○ interface{}の型エイリアス ● [T fmt.Stringer]はTをfmt.Stringer型を実装した型に限る 20 type Hex int func (h Hex) String() string { return fmt.Sprintf("%x", int(h)) } func Print[T fmt.Stringer](s []T) { for _, v := range s { fmt.Printf("0x%s\n", v.String()) } } func main() { Print([]Hex{100, 200}) } Playgroundで動かす 制約

Slide 21

Slide 21 text

組み込みの制約 ■ 比較可能な型を表すcomparable ● 定義することが不可能のため組み込みである必要がある ● マップのキーにできる 21 func Keys[K comparable, V any](m map[K]V) []K { keys := make([]K, 0, len(m)) for k := range m { keys = append(keys, k) } return keys } func main() { m := map[string]int{"cat": 3, "dog": 2} fmt.Println(Keys(m)) // [cat dog] } Playgroundで動かす

Slide 22

Slide 22 text

制約と型セット ■ インタフェース要素が拡張される ● https://tip.golang.org/ref/spec#Interface_types ● TypeElem:int | stringのように記述できるようになった ● UnderlyingType: ~intや~stringのように記述 ● 制約以外に使えるインタフェース(今までのインタフェース) ○ MethodElemおよびTypeElemがインタフェースのみからなる 22 参考:https://zenn.dev/nobishii/articles/99a2b55e2d3e50 InterfaceType = "interface" "{" { InterfaceElem ";" } "}" . InterfaceElem = MethodElem | TypeElem . MethodElem = MethodName Signature . MethodName = identifier . TypeElem = TypeTerm { "|" TypeTerm } . TypeTerm = Type | UnderlyingType . UnderlyingType = "~" Type .

Slide 23

Slide 23 text

Underlying type ■ すべての型はUnderlying typeを持つ ● int型やstring型などの組み込み型は自分自身 ● 型リテラルで記述された型も自分自身 ● Defined typeはtype X YのYのUnderlying type ● 型階層が作れないようになっている ● Underlying typeのメソッドセットを引き継ぐ ○ 例:type I interface{ F() }、type T I 23 int []string struct {N int} X type X struct {N int} Y type Y X 参考:https://speakerdeck.com/dqneo/go-language-underlying-type

Slide 24

Slide 24 text

型セットとインタフェース ■ インタフェースを実装するとは ● その型セットの要素であること 24 インタフェース 型セット interface{} すべての型の集合 interface{ e1; e2 } インタフェース要素e1とe2の型セットの共通部分 interface{ m1(); m2() } m1とm2をメソッドセットに持つ型の集合 interface{ T } 型Tだけからなる集合(型 Tは非インタフェース) interface{ ~T } 型Tのunderlying typeからなる集合 interface { t1 | t2 } Type term t1とt2の型セットの和集合

Slide 25

Slide 25 text

interface{ … }の省略 ■ 制約で省略して記述できる ● https://go.dev/issue/48424 ● constraintsパッケージで提供する制約を減らせる ○ 新しく導入される制約を提供するパッケージ // interface{ ~map[K] V }を~map[K] Vと書ける func Keys[M ~map[K]V, K comparable, V any](m M) []K { keys := make([]K, 0, len(m)) for k := range m { keys = append(keys, k) } return keys } func main() { m := url.Values{"a":{"v1"}, "b":{"v2"}} fmt.Println(Keys(m)) // [a b] } Playgroundで動かす

Slide 26

Slide 26 text

Fuzzing

Slide 27

Slide 27 text

ファジングとは ■ ランダムに大量の入力データを与えるテスト手法 ● go-fuzzが有名 ○ 標準パッケージやコンパイラのバグを多く発見している ● コーパスと呼ばれるデータ群からデータを生成する ● 手入力で作ったテストデータでは難しいデータを作成 ○ 大量のデータ ○ バイアスのかかってないデータ ● 予期しない入力によりクラッシュする可能性をテストする ○ パニック ● スタックオーバーフロー ● インデックスが範囲を超える

Slide 28

Slide 28 text

Go1.18で導入される機能 ■ go testでファジングが利用できる ● http://go.dev/doc/fuzz ● 環境構築不要でファジングができる ○ 実装がすべてGoで標準で用意される ● testing.Fの追加 ○ シードコーパスの追加 ○ テスト関数の指定 ● go test -fuzzでファジングを実行 ○ デフォルトでは実行されないようにする ● 通常はマシンパワーを使って長時間実行されるため ● Rob Pike氏のコメント ● すでにいくつかバグを見つけてるみたい ○ Fuzzing trophy case - Go Wiki

Slide 29

Slide 29 text

テスト関数の書き方 ■ testing.Fを用いる ● (*testing.F).Addメソッドでシードコーパスを追加 ○ 基本型 ● (*testing.F).Fuzzメソッドでテスト関数を指定 ○ 引数に入力データが与えられる func FuzzFibo(f *testing.F) { f.Add(2) f.Fuzz(func(t *testing.T, n int) { if Fibo(n) != Fibo(n-2)+Fibo(n-1) { t.Fatal("NG") } }) }

Slide 30

Slide 30 text

実行の仕方 ■ go testコマンドに-fuzzオプションを指定する ● -fuzzオプションには正規表現が指定できる ● -fuzzオプションを指定しない場合はシードコーパスのみでテスト ○ testdata/corpus以下も読み込む ● testdata/corpus以下のコーパスも使用される $ go test -fuzz FuzzFibo -fuzztime MAXの実行時間。デフォルトは無限。 -keepfuzzing クラッシュしても続けるか その他 -race、-parallel、-runなども対応

Slide 31

Slide 31 text

Workspace mode

Slide 32

Slide 32 text

Workspace mode ■ 複数のモジュールを扱うモード ● go.workファイルにモジュールのあるディレクトリの一覧を書く ● ビルドにはgo.workにあるモジュールが使われる ○ go getが不要になる ○ go.modファイルのreplaceディレクティブで書き換える必要なし go 1.18 use ( moda modb )

Slide 33

Slide 33 text

go work init ■ go.workファイルを初期化する ● モジュールディレクトリを指定して初期化する ● Go Modules管理されたディレクトリのみ指定できる ○ 管理されてないディレクトリは指定しても無視される $ go work init moda modb

Slide 34

Slide 34 text

go work use ■ useディレクティブにモジュールを追加 ● 追加したいモジュールディレクトリを指定する $ go work use modc

Slide 35

Slide 35 text

デモ

Slide 36

Slide 36 text

まとめ ■ Go1.18のリリースが楽しみですね! ● リリースパーティではさらに詳しい話が聞けます! ● まずはベータ版をインストールして試してみましょう!