Slide 1

Slide 1 text

new(1.26) ← これすき id:utgwkk / @utgwkk (うたがわきき) 2026/2/27 kamakura.go #8 1

Slide 2

Slide 2 text

自己紹介 ● うたがわきき (@utgwkk) ● 株式会社はてな ○ Webアプリケーションエンジニア ○ 京都在住 ● 好きなパッケージはreflect 2

Slide 3

Slide 3 text

3 合計 88 枚のスライド 300 / 88 = 3.41

Slide 4

Slide 4 text

new(1.25) 4

Slide 5

Slide 5 text

5 Go 1.25までのnew関数 ● 指定された型のポインタを返す ○ buf := new(bytes.Buffer) ● 引数に式を渡せない

Slide 6

Slide 6 text

涙ぐましいコード こういうコードを書いたことがある人 ? str := "string" strPtr := &str 6

Slide 7

Slide 7 text

便利関数でしのぐ こういう関数を書いたことがある人 ? func Ptr[T any](t T) *T { return &t } 7

Slide 8

Slide 8 text

世界がポインタを作りたがっている ● aws.String (aws-sdk-go-v2) ● lo.ToPtr (samber/lo) ● などなど ● ポインタを作るニーズが絶えない 8

Slide 9

Slide 9 text

2026/2/10 (JSTでは2/11) 9

Slide 10

Slide 10 text

🎉Go 1.26 Released 🎉 10

Slide 11

Slide 11 text

new(1.26) 11

Slide 12

Slide 12 text

Go 1.26のnew関数 new関数の引数として式を渡せるようになった 12

Slide 13

Slide 13 text

涙ぐましいコード (before) str := "string" strPtr := &str 13

Slide 14

Slide 14 text

全てが輝いて見える (after) strPtr := new("string") 14

Slide 15

Slide 15 text

サイコ〜〜〜〜〜 15

Slide 16

Slide 16 text

今すぐ func[T](T) *T を捨てる ● go fixコマンドで実現できる 16

Slide 17

Slide 17 text

今すぐ func[T](T) *T を捨てる ● go.modにgo 1.26って書く ● go fix -newexpr を実行する ○ 既存の呼び出しも置き換えてくれる ○ //go:fix inlineコメントも付け足す 17

Slide 18

Slide 18 text

//go:fix inlineってなに ● 関数をインライン化するためのdirective ● go fix -inlineコマンドでインライン化できる 18

Slide 19

Slide 19 text

go fix -inlineの注意点 ● ジェネリクスの型引数を渡した関数呼び出し しかインライン化されない ○ Ptr[int](1) はインライン化される ○ Ptr(1) はインライン化されない ○ go fix -newexpr ではインライン化される ● 現状そういう制約らしい 19

Slide 20

Slide 20 text

go fix -newexprなどの仕組み ● modernizeパッケージによって実現 ● ルールごとの静的解析器 (Analyzer) の実装が ある ○ すごくざっくり言うとfixできるESLintのルール 20

Slide 21

Slide 21 text

modernizeパッケージを見る ● Analyzerがある=モダンな書き方ができる ● 全人類見たほうがいい ● ということで…… 21

Slide 22

Slide 22 text

modernizeパッケージの Analyzer全部見る id:utgwkk / @utgwkk (うたがわきき) 2026/2/27 kamakura.go #8 22

Slide 23

Slide 23 text

はじめに ● スライド公開するので時間が足りなかった分 はそっちを見てください 23

Slide 24

Slide 24 text

24個のAnalyzer ● appendclipped ● bloop ● any ● errorsastype ● fmtappendf ● forvar ● mapsloop ● minmax ● newexpr ● omitzero ● plusbuild ● rangeint 24 ● reflecttypefor ● slicescontains ● slicesdelete ● slicessort ● stditerators ● stringscut ● stringscutprefix ● stringsseq ● stringsbuilder ● testingcontext ● unsafefuncs ● waitgroup

Slide 25

Slide 25 text

appendclipped ● スライスの結合をシンプルにする ○ append関数→slices.Concat ● Go 1.22 25

Slide 26

Slide 26 text

appendclipped (before) append(append(s, s1...), s2...) 26

Slide 27

Slide 27 text

appendclipped (after) slices.Concat(s, s1, s2) 27

Slide 28

Slide 28 text

bloop ● ベンチマークで b.N ではなく b.Loop() の値 を使うforループに書き換える ● Go 1.24 ● デフォルトでは無効 ○ ベンチマーク結果が変わりうる 28

Slide 29

Slide 29 text

bloop (before) for i := 0; i < b.N; i++ { // benchmark code } 29

Slide 30

Slide 30 text

bloop (after) for range b.Loop() { // benchmark code } 30

Slide 31

Slide 31 text

any ● interface{} をanyに置き換える ● Go 1.18 31

Slide 32

Slide 32 text

errorsastype ● errors.Asをerrors.AsTypeに置き換える ○ errors.AsTypeはerrors.Asのジェネリック版 ● Go 1.26 32

Slide 33

Slide 33 text

errorsastype (before) var myErr *MyError if errors.As(err, &myErr) { // myErr != nil } 33

Slide 34

Slide 34 text

errorsastype (after) if myErr := errors.AsType[*MyError](err); myErr != nil { // myErr != nil } 34

Slide 35

Slide 35 text

fmtappendf ● []byte(fmt.Sprintf(...)) を fmt.Appendf() に置き換える ○ アロケーションが減って嬉しい ● Go 1.19 35

Slide 36

Slide 36 text

fmtappendf (before) []byte(fmt.Sprintf("%s", x)) 36

Slide 37

Slide 37 text

fmtappendf (after) fmt.Appendf(nil, "%s", x) 37

Slide 38

Slide 38 text

forvar ● ループ変数のシャドウイングをなくす ○ 並行処理でループ変数が書き換えられるのを防ぐ 過去のイディオム ● Go 1.22 38

Slide 39

Slide 39 text

forvar (before) var wg sync.WaitGroup for i := range 10 { i := i wg.Go(func() { fmt.Println(i) }) } wg.Wait() 39

Slide 40

Slide 40 text

forvar (after) var wg sync.WaitGroup for i := range 10 { wg.Go(func() { fmt.Println(i) }) } wg.Wait() 40

Slide 41

Slide 41 text

mapsloop ● mapに対するループをmapsパッケージの 関数呼び出しに置き換える ○ 文脈に応じて置換後の関数は変わる ■ maps.Copy, maps.Insert, maps.Clone, maps.Collect ● Go 1.23 41

Slide 42

Slide 42 text

mapsloop (before) y := make(map[string]int) for k, v := range x { y[k] = v } 42

Slide 43

Slide 43 text

mapsloop (after) maps.Copy(y, x) 43

Slide 44

Slide 44 text

minmax ● if-elseによる最小値・最大値の計算を min,max関数に置き換える ● Go 1.21 44

Slide 45

Slide 45 text

minmax (before) var x int if a < b { x = a } else { x = b } 45

Slide 46

Slide 46 text

minmax (after) var x int x = min(a, b) 46

Slide 47

Slide 47 text

newexpr ● func [T](t T) *T というシグネチャを持つ関 数呼び出しをnew関数に置き換える ○ //go:fix inlineコメントも足す ● Go 1.26 47

Slide 48

Slide 48 text

omitzero ● encoding/jsonのstruct tagのomitemptyを omitzeroに置き換える ● Go 1.24 ● デフォルトでは無効 ○ omitemptyとomitzeroは微妙に意味が変わる 48

Slide 49

Slide 49 text

pluszero ● //+build 形式のビルドコメントを //go:build 形式に置き換える ● Go 1.18 49

Slide 50

Slide 50 text

rangeint ● for i := 0; i < n; i++ { ... } というforループ を for i := range n { ... } に置き換える ● Go 1.22 50

Slide 51

Slide 51 text

reflecttypefor ● reflect.TypeOfをreflect.TypeForに 置き換える ○ ジェネリクス版 ○ interfaceのreflect.Typeが取りやすくなった ● Go 1.26 ● ランタイムの型が不定の場合は置き換えない 51

Slide 52

Slide 52 text

reflecttypefor (before) t := reflect.TypeOf((*error)(nil)) 52

Slide 53

Slide 53 text

reflecttypefor (after) t := reflect.TypeFor[error]() 53

Slide 54

Slide 54 text

slicescontains ● forループでスライスの要素を探すコードを slices.Contains, slices.ContainsFuncに 置き換える ● Go 1.21 54

Slide 55

Slide 55 text

slicescontains (before) for _, x := range xs { if x == 0 { return true } } return false 55

Slide 56

Slide 56 text

slicescontains (after) return slices.Contains(xs, 0) 56

Slide 57

Slide 57 text

slicesdelete ● スライスの特定範囲の要素を削除するコード をslices.Deleteに置き換える ● Go 1.21 ● デフォルトでは無効 ○ 書き換え元のコードとメモリの取り扱いが異なる 57

Slide 58

Slide 58 text

slicesdelete (before) s = append(s[:i], s[j:]...) 58

Slide 59

Slide 59 text

slicesdelete (after) s = slices.Delete(s, i, j) 59

Slide 60

Slide 60 text

slicessort ● sort.Slice関数によるソートを slices.Sort関数に置き換える ● Go 1.21 60

Slide 61

Slide 61 text

slicessort (before) sort.Slice(s, func(i, j int) bool { return s[i] < s[j] }) 61

Slide 62

Slide 62 text

slicessort (after) slices.Sort(s) 62

Slide 63

Slide 63 text

stditerators ● Len/At形式のAPIをiteratorに置き換える ○ go/typesやreflectパッケージなど ● Go 1.23 63

Slide 64

Slide 64 text

stditerators (before) for i := 0; i < x.Len(); i++ { use(x.At(i)) } 64

Slide 65

Slide 65 text

stditerators (after) for elem := range x.All() { use(x.At(i)) } 65

Slide 66

Slide 66 text

stringscut ● strings.Indexを使った文字列を分けるコード をstrings.Cutに置き換える ● Go 1.18 66

Slide 67

Slide 67 text

stringscut (before) idx := strings.Index(s, substr) if idx >= 0 { return s[:idx] } 67

Slide 68

Slide 68 text

stringscut (after) before, _, ok := strings.Cut(s, substr) if ok { return before } 68

Slide 69

Slide 69 text

stringscutprefix ● strings.HasPrefixとstrings.TrimPrefixを組 み合わせたコードをstrings.CutPrefixに置き 換える ○ suffixも同様 (strings.CutSuffixに置き換える) ● Go 1.20 69

Slide 70

Slide 70 text

stringscutprefix (before) if strings.HasPrefix(s, prefix) { use(strings.TrimPrefix(s, prefix)) } 70

Slide 71

Slide 71 text

stringscutprefix (after) if after, ok := strings.CutPrefix(s, prefix); ok { use(after) } 71

Slide 72

Slide 72 text

stringsseq ● 文字列をsplitしてforループするコードでの strings.Splitの呼び出しをstrings.SplitSeq に置き換える ○ iteratorになるので効率がよい ● Go 1.24 72

Slide 73

Slide 73 text

stringsbuilder ● +=演算子による文字列結合を strings.Builderによる文字列組み立てに置き 換える ● Go 1.10 73

Slide 74

Slide 74 text

stringsbuilder (before) var s = "[" for x := range seq { s += x s += "." } s += "]" use(s) 74

Slide 75

Slide 75 text

stringsbuilder (after) var s strings.Builder s.WriteString("[") for x := range seq { s.WriteString(x) s.WriteString(".") } s.WriteString("]") use(s.String()) 75

Slide 76

Slide 76 text

testingcontext ● テスト中でcancelされるcontextを作るコー ドを t.Context メソッドに置き換える ○ t.Contextが返すcontextはテスト終了時に 自動的に中断される ● Go 1.24 76

Slide 77

Slide 77 text

testingcontext (before) ctx, cancel := context.WithCancel(context.Background()) defer cancel() 77

Slide 78

Slide 78 text

testingcontext (after) ctx := t.Context() 78

Slide 79

Slide 79 text

unsafefuncs ● unsafe.Pointerとuintptrによるポインタ演 算をunsafe.Addに置き換える ● Go 1.17 ● 普通に過ごしていたら世話にならないのでは ○ 世話にならないといいですね 79

Slide 80

Slide 80 text

unsafefuncs (before) unsafe.Pointer(uintptr(ptr) + uintptr(n)) 80

Slide 81

Slide 81 text

unsafefuncs (after) unsafe.Add(ptr, n) 81

Slide 82

Slide 82 text

waitgroup ● sync.WaitGroupを使った並行処理のコード をシンプルに書き換える ○ wg.Addとwg.Doneのタイミングに気をつけていた ○ wg.Goメソッドを呼ぶだけでよくなる ● Go 1.25 82

Slide 83

Slide 83 text

waitgroup (before) wg.Add(1) go func() { defer wg.Done() ... }() 83

Slide 84

Slide 84 text

waitgroup (after) wg.Go(func(){ ... }) 84

Slide 85

Slide 85 text

おつかれさまでした ● appendclipped ● bloop ● any ● errorsastype ● fmtappendf ● forvar ● mapsloop ● minmax ● newexpr ● omitzero ● plusbuild ● rangeint 85 ● reflecttypefor ● slicescontains ● slicesdelete ● slicessort ● stditerators ● stringscut ● stringscutprefix ● stringsseq ● stringsbuilder ● testingcontext ● unsafefuncs ● waitgroup

Slide 86

Slide 86 text

参考 ● godoc ○ fmt ○ encoding/json ○ errors ○ golang.org/x/tools/go/analysis/passes/modernize ○ maps ○ reflect ○ slices ○ strings ○ sync ○ testing ○ unsafe 86

Slide 87

Slide 87 text

参考 ● Go 1.26 Release Notes - The Go Programming Language ● Using go fix to modernize Go code - The Go Programming Language ● Go 1.26で go fix が面白くなった | フューチャー技術ブログ ● go fix -inlineで型引数を省略した関数呼び出しがインライン 化できないことを示すコード 87

Slide 88

Slide 88 text

まとめ ● new(1.26) をどうぞよろしくお願いします ● modernizeパッケージが提供するAnalyzerを 知ってモダンなGoのコードを書きましょう ○ LLMが古いコードを吐いてくるのでモダンなコードを 浸透させる必要がある、みたいな話がGo Blogに書い てある 88