Go
Go言語 完全概要ガイド
1. はじめに
1.1 Go言語とは
Go(別名Golang)は、2009年にGoogleのRobert Griesemer、Rob Pike、Ken Thompsonによって設計・開発されたオープンソースのプログラミング言語である。C言語の伝統を受け継ぎつつ、現代的なソフトウェア開発の課題——特に並行処理、大規模コードベースの管理、高速なコンパイル——に対応するために生まれた。
Goは「シンプルさ」を最大の設計原則としている。言語仕様は比較的小さく、キーワードはわずか25個。これは、Java(約50個)やC++(約90個)と比較して圧倒的に少ない。この設計思想により、チーム開発において全員が同じスタイルでコードを書き、読みやすいコードベースを維持できる。
1.2 Go言語の歴史と背景
Goの開発は2007年にGoogleの社内プロジェクトとして始まった。当時のGoogleでは、数百万行規模のC++やJavaのコードベースに対して、コンパイル時間の長さや依存関係の複雑さが深刻な問題となっていた。
主要なマイルストーン:
| 年 | イベント |
|---|---|
| 2007年 | Google社内で開発開始 |
| 2009年11月 | オープンソースとして公開 |
| 2012年3月 | Go 1.0リリース(後方互換性の保証開始) |
| 2015年8月 | Go 1.5リリース(セルフホスティング化、GC大幅改善) |
| 2018年8月 | Go 1.11リリース(Go Modules導入) |
| 2022年3月 | Go 1.18リリース(Generics導入) |
| 2023年8月 | Go 1.21リリース(組み込み関数min/max/clear追加) |
| 2024年2月 | Go 1.22リリース(ループ変数のスコープ修正) |
| 2025年2月 | Go 1.24リリース(ジェネリクスの型推論強化) |
1.3 Go言語の主な特徴
Goが他の言語と差別化される主要な特徴は以下の通りである:
- 静的型付け: コンパイル時に型チェックが行われ、実行時エラーを未然に防ぐ
- ガベージコレクション: メモリ管理を自動化し、メモリリークのリスクを低減
- ネイティブコンパイル: マシンコードに直接コンパイルされ、高いパフォーマンスを実現
- 組み込み並行処理: Goroutineとチャネルによる軽量な並行処理モデル
- 高速コンパイル: 大規模プロジェクトでも数秒でコンパイル完了
- クロスコンパイル: 1つの環境から複数のOS/アーキテクチャ向けバイナリを生成
- シングルバイナリ: 依存ライブラリを含む単一の実行ファイルを生成
- 標準ライブラリの充実: HTTP サーバー、JSON処理、暗号化など豊富な標準パッケージ
1.4 Go言語のユースケース
Goは以下の分野で特に強力である:
- Webサービス/API: 高性能なHTTPサーバーの構築
- マイクロサービス: 小さなバイナリサイズと高速起動時間
- クラウドインフラ: Docker、Kubernetes、Terraform等はGoで開発
- CLI ツール: クロスプラットフォームの単一バイナリを生成
- ネットワークプログラミング: TCP/UDP、gRPC等の通信基盤
- 分散システム: etcd、Consul等の分散KVS/サービスディスカバリ
- DevOps ツール: CI/CDパイプライン、監視ツール
Goで書かれた代表的なプロジェクト:
| プロジェクト | 説明 |
|---|---|
| Docker | コンテナランタイム |
| Kubernetes | コンテナオーケストレーション |
| Terraform | Infrastructure as Code |
| Prometheus | モニタリング/アラート |
| etcd | 分散KVS |
| Hugo | 静的サイトジェネレータ |
| CockroachDB | 分散SQL DB |
| Istio | サービスメッシュ |
2. Goのアーキテクチャと設計思想
2.1 言語設計の原則
Goの設計は3つの根本原則に基づいている:
シンプルさ(Simplicity)
Goは機能を増やすのではなく、機能を削ることで設計されている。三項演算子(? :)、例外処理(try/catch)、継承、オーバーロードなど、他の言語では一般的な機能が意図的に排除されている。
直交性(Orthogonality) 言語の各機能は独立しており、組み合わせることで複雑な処理を表現できる。例えば、インターフェースは暗黙的に実装され(構造的型付け)、これにより柔軟なポリモーフィズムを実現する。
コンポジション(Composition over Inheritance) Goにはクラス継承がない。代わりに、構造体の埋め込み(embedding)とインターフェースによるコンポジションを採用し、コードの再利用と柔軟性を両立する。
2.2 コンパイラアーキテクチャ
Goのコンパイラ(gc)は以下のフェーズでソースコードを処理する:
ソースコード (.go)
↓
[字句解析 / Lexer]
↓
[構文解析 / Parser] → AST (抽象構文木)
↓
[型チェック / Type Checker]
↓
[中間表現生成 / SSA (Static Single Assignment)]
↓
[最適化パス / Optimization Passes]
↓
[コード生成 / Code Generation]
↓
マシンコード (実行ファイル)
Go 1.5以降、コンパイラ自体がGoで書かれている(セルフホスティング)。以前はCで記述されていたが、ブートストラップ問題を解決し、Go自体の最適化がコンパイラにも適用されるようになった。
2.3 ランタイムアーキテクチャ
Goのランタイムは実行ファイルに組み込まれ、以下の機能を提供する:
Goroutineスケジューラ(GMP モデル)
GoのスケジューラはGMP(Goroutine, Machine, Processor)モデルに基づいている:
G (Goroutine): 実行される関数単位。軽量スレッド。
M (Machine): OSスレッド。実際のCPUコアで実行される。
P (Processor): 論理プロセッサ。GOMAXPROCSで設定。
┌─────────────────────────────────────────┐
│ Go Scheduler │
│ │
│ ┌───┐ ┌───┐ ┌───┐ │
│ │ P │ │ P │ │ P │ (GOMAXPROCS=3) │
│ └─┬─┘ └─┬─┘ └─┬─┘ │
│ │ │ │ │
│ ┌─┴─┐ ┌─┴─┐ ┌─┴─┐ │
│ │ M │ │ M │ │ M │ (OS Threads) │
│ └─┬─┘ └─┬─┘ └─┬─┘ │
│ │ │ │ │
│ ┌─┴─┐ ┌─┴─┐ ┌─┴─┐ │
│ │ G │ │ G │ │ G │ (Goroutines) │
│ │ G │ │ G │ │ │ │
│ │ G │ │ │ │ │ │
│ └───┘ └───┘ └───┘ │
│ │
│ Global Run Queue: [G] [G] [G] ... │
└─────────────────────────────────────────┘
各Pはローカルランキュー(最大256個のG)を持ち、Mと1対1で結合される。Pに割り当てられたGが実行される。あるPのローカルキューが空になると、他のPからワークスティーリングを行う。
ガベージコレクタ
Go 1.5以降、並行マーク・スイープ(Concurrent Mark-and-Sweep)GCを採用している:
[アプリケーション実行]
│
[GCトリガー] ← ヒープサイズが閾値を超えた時
│
[STW (Stop The World) - Phase 1] ← 非常に短い(マイクロ秒単位)
│ GCルートのスキャン開始
│
[並行マーキング] ← アプリケーションと並行で実行
│ ライトバリアを使用して新規割り当てを追跡
│
[STW (Stop The World) - Phase 2] ← 非常に短い
│ マーキング完了の確認
│
[並行スイーピング] ← アプリケーションと並行で実行
│ 未使用メモリの回収
│
[アプリケーション実行再開]
GCの目標レイテンシはサブミリ秒のSTW時間。GOGC環境変数でGCの頻度を制御でき、デフォルトは100(ヒープが前回GC後の2倍になったらGCを実行)。
# GCの頻度を調整
GOGC=200 ./myapp # GC頻度を下げる(メモリ使用量は増える)
GOGC=50 ./myapp # GC頻度を上げる(レイテンシ改善)
GOGC=off ./myapp # GCを無効化(非推奨)
# Go 1.19以降: メモリ上限を設定
GOMEMLIMIT=1GiB ./myapp
2.4 メモリモデル
Goのメモリモデルは、並行プログラムにおける変数の可視性を定義する。主要な概念:
- happens-before関係: ある操作が別の操作の前に完了することが保証される関係
- 同期プリミティブ: チャネル操作、sync.Mutex、sync.Once、atomic操作等が happens-before 関係を確立する
- データ競合: 2つ以上のGoroutineが同じ変数に同時にアクセスし、少なくとも1つが書き込みである場合に発生
// データ競合の例(不正なコード)
var count int
go func() {
count++ // 書き込み
}()
fmt.Println(count) // 読み取り — データ競合!
// 正しい実装: sync.Mutexを使用
var mu sync.Mutex
var count int
go func() {
mu.Lock()
count++
mu.Unlock()
}()
mu.Lock()
fmt.Println(count)
mu.Unlock()
3. インストールとセットアップ
3.1 インストール方法
macOS
# Homebrewを使用
brew install go
# または公式インストーラをダウンロード
# https://go.dev/dl/ から .pkg ファイルをダウンロードして実行
Linux
# 公式バイナリを使用(推奨)
wget https://go.dev/dl/go1.24.2.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.24.2.linux-amd64.tar.gz
# PATHに追加(~/.bashrc または ~/.zshrc)
export PATH=$PATH:/usr/local/go/bin
export PATH=$PATH:$(go env GOPATH)/bin
Windows
# wingetを使用
winget install GoLang.Go
# またはScoopを使用
scoop install go
# または公式インストーラ (.msi) をダウンロード
3.2 環境変数と設定
Goの動作は環境変数で制御される:
# 主要な環境変数を確認
go env
# 重要な環境変数
GOROOT=/usr/local/go # Goのインストールディレクトリ
GOPATH=$HOME/go # ワークスペースディレクトリ
GOBIN=$GOPATH/bin # インストールされるバイナリの場所
GOPROXY=https://proxy.golang.org,direct # モジュールプロキシ
GONOSUMCHECK= # チェックサム検証をスキップするモジュール
GOPRIVATE= # プライベートモジュールのパターン
GOMAXPROCS= # 並行実行に使用するCPUコア数
# プライベートリポジトリの設定例
go env -w GOPRIVATE=github.com/mycompany/*
go env -w GONOSUMCHECK=github.com/mycompany/*
go env -w GOPROXY=https://proxy.golang.org,direct
3.3 プロジェクト構造
Goプロジェクトの標準的なディレクトリ構造:
myproject/
├── go.mod # モジュール定義ファイル
├── go.sum # 依存関係のチェックサム
├── main.go # エントリーポイント
├── cmd/ # メインアプリケーション
│ ├── api/
│ │ └── main.go
│ └── worker/
│ └── main.go
├── internal/ # プライベートパッケージ(外部から参照不可)
│ ├── auth/
│ │ ├── auth.go
│ │ └── auth_test.go
│ ├── database/
│ │ ├── database.go
│ │ └── database_test.go
│ └── middleware/
│ └── middleware.go
├── pkg/ # 外部公開パッケージ
│ └── utils/
│ └── utils.go
├── api/ # API定義(OpenAPI, protobuf等)
│ └── v1/
│ └── api.proto
├── configs/ # 設定ファイル
│ └── config.yaml
├── scripts/ # ビルド・デプロイスクリプト
│ └── setup.sh
├── test/ # 追加テストデータ
│ └── testdata/
├── docs/ # ドキュメント
├── Dockerfile
├── Makefile
└── README.md
3.4 モジュールの初期化と管理
# 新しいモジュールの初期化
go mod init github.com/username/myproject
# 依存関係の追加
go get github.com/gin-gonic/gin@latest
go get github.com/lib/pq@v1.10.9
# 依存関係の整理(未使用の削除、不足分の追加)
go mod tidy
# 依存関係のダウンロード
go mod download
# 依存関係のベンダリング
go mod vendor
# 依存関係のグラフ表示
go mod graph
# 依存関係の検証
go mod verify
go.modファイルの例:
module github.com/username/myproject
go 1.24
require (
github.com/gin-gonic/gin v1.9.1
github.com/lib/pq v1.10.9
github.com/redis/go-redis/v9 v9.4.0
go.uber.org/zap v1.27.0
google.golang.org/grpc v1.62.0
google.golang.org/protobuf v1.33.0
)
require (
// 間接依存関係(自動管理)
github.com/bytedance/sonic v1.11.2 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
)
4. 基本構文とデータ型
4.1 変数宣言と基本型
package main
import "fmt"
func main() {
// === 変数宣言 ===
// var キーワードによる宣言
var name string = "Go"
var version float64 = 1.24
var isStable bool = true
// 型推論(型を省略)
var count = 42 // int
var message = "Hello, World" // string
// 短縮宣言(関数内のみ使用可能)
language := "Go"
year := 2009
// 複数変数の同時宣言
var (
host string = "localhost"
port int = 8080
debug bool = false
)
// ゼロ値(デフォルト値)
var i int // 0
var f float64 // 0.0
var s string // "" (空文字列)
var b bool // false
var p *int // nil
var sl []int // nil
// 定数
const Pi = 3.14159265358979
const (
StatusOK = 200
StatusNotFound = 404
StatusError = 500
)
// iota(列挙定数の自動生成)
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
// ビットフラグでのiota活用
const (
ReadPermission = 1 << iota // 1
WritePermission // 2
ExecutePermission // 4
)
fmt.Println(name, version, isStable, count, message)
fmt.Println(language, year, host, port, debug)
fmt.Println(i, f, s, b, p, sl)
}
4.2 基本データ型
// === 数値型 ===
var i8 int8 // -128 ~ 127
var i16 int16 // -32768 ~ 32767
var i32 int32 // -2147483648 ~ 2147483647
var i64 int64 // -9223372036854775808 ~ 9223372036854775807
var i int // プラットフォーム依存(32bit or 64bit)
var u8 uint8 // 0 ~ 255(byte のエイリアス)
var u16 uint16 // 0 ~ 65535
var u32 uint32 // 0 ~ 4294967295
var u64 uint64 // 0 ~ 18446744073709551615
var ui uint // プラットフォーム依存
var f32 float32 // IEEE 754 32bit浮動小数点
var f64 float64 // IEEE 754 64bit浮動小数点
var c64 complex64 // float32の実部と虚部
var c128 complex128 // float64の実部と虚部
// === 文字列型 ===
var s string = "Hello, 世界" // UTF-8エンコード
// 生の文字列リテラル(エスケープなし)
var raw string = `{
"name": "Go",
"version": 1.24
}`
// runeはint32のエイリアス(Unicodeコードポイント)
var r rune = '世' // 19990
// === ポインタ型 ===
x := 42
ptr := &x // *int 型のポインタ
fmt.Println(*ptr) // 42(デリファレンス)
*ptr = 100
fmt.Println(x) // 100
4.3 複合データ型
// === 配列(固定長) ===
var arr [5]int = [5]int{1, 2, 3, 4, 5}
arr2 := [...]int{1, 2, 3} // コンパイラが長さを推論
// === スライス(可変長) ===
slice := []int{1, 2, 3, 4, 5}
slice = append(slice, 6, 7) // 要素の追加
// makeでの作成(長さ, 容量)
s := make([]int, 0, 10) // 長さ0, 容量10
// スライシング
sub := slice[1:3] // [2, 3](インデックス1から2まで)
sub2 := slice[:3] // [1, 2, 3](先頭から2まで)
sub3 := slice[2:] // [3, 4, 5, 6, 7](インデックス2から末尾)
// === マップ(連想配列) ===
m := map[string]int{
"apple": 100,
"banana": 200,
"cherry": 300,
}
// 要素のアクセスと存在チェック
value, exists := m["apple"]
if exists {
fmt.Println(value) // 100
}
// 要素の追加と削除
m["grape"] = 400
delete(m, "banana")
// マップのイテレーション(順序は保証されない)
for key, value := range m {
fmt.Printf("%s: %d\n", key, value)
}
// === 構造体 ===
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
IsActive bool `json:"is_active"`
}
user := User{
ID: 1,
Name: "Alice",
Email: "alice@example.com",
CreatedAt: time.Now(),
IsActive: true,
}
// 構造体の埋め込み(コンポジション)
type Admin struct {
User // 埋め込みフィールド
Role string `json:"role"`
Level int `json:"level"`
}
admin := Admin{
User: User{
ID: 2,
Name: "Bob",
},
Role: "superadmin",
Level: 10,
}
// 埋め込まれたフィールドに直接アクセス可能
fmt.Println(admin.Name) // "Bob"
5. 制御構文とフロー
5.1 条件分岐
// if文
if x > 0 {
fmt.Println("正の数")
} else if x < 0 {
fmt.Println("負の数")
} else {
fmt.Println("ゼロ")
}
// if文での変数初期化(スコープはifブロック内)
if err := doSomething(); err != nil {
log.Printf("エラー発生: %v", err)
return err
}
// switch文(breakは不要、自動的にfall throughしない)
switch day {
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
fmt.Println("平日")
case "Saturday", "Sunday":
fmt.Println("週末")
default:
fmt.Println("不明な曜日")
}
// 条件なしswitch(if-else chainの代替)
switch {
case score >= 90:
grade = "A"
case score >= 80:
grade = "B"
case score >= 70:
grade = "C"
default:
grade = "F"
}
// 型switch
func describe(i interface{}) string {
switch v := i.(type) {
case int:
return fmt.Sprintf("整数: %d", v)
case string:
return fmt.Sprintf("文字列: %s", v)
case bool:
return fmt.Sprintf("真偽値: %t", v)
default:
return fmt.Sprintf("不明な型: %T", v)
}
}
5.2 ループ
Goにはforループのみが存在する(while, do-whileはない):
// 標準的なforループ
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// whileスタイル
count := 0
for count < 10 {
count++
}
// 無限ループ
for {
data, err := reader.Read()
if err == io.EOF {
break
}
process(data)
}
// rangeによるイテレーション
fruits := []string{"apple", "banana", "cherry"}
for index, value := range fruits {
fmt.Printf("%d: %s\n", index, value)
}
// インデックスのみ
for i := range fruits {
fmt.Println(i)
}
// 値のみ(インデックス不要)
for _, fruit := range fruits {
fmt.Println(fruit)
}
// マップのイテレーション
for key, value := range myMap {
fmt.Printf("%s = %v\n", key, value)
}
// チャネルのイテレーション
for msg := range messageChan {
process(msg)
}
// 文字列のイテレーション(runeごと)
for i, r := range "Hello, 世界" {
fmt.Printf("index=%d, rune=%c, code=%d\n", i, r, r)
}
5.3 defer, panic, recover
// defer: 関数終了時に実行(LIFO順)
func readFile(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close() // 関数終了時にファイルを必ず閉じる
return io.ReadAll(f)
}
// 複数のdefer(後入れ先出し)
func example() {
defer fmt.Println("1st")
defer fmt.Println("2nd")
defer fmt.Println("3rd")
// 出力: 3rd, 2nd, 1st
}
// panic と recover
func safeDivide(a, b float64) (result float64, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("パニック回復: %v", r)
}
}()
if b == 0 {
panic("ゼロ除算")
}
return a / b, nil
}
6. 関数とエラーハンドリング
6.1 関数の定義
// 基本的な関数
func add(a, b int) int {
return a + b
}
// 複数の戻り値
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("ゼロ除算はできません")
}
return a / b, nil
}
// 名前付き戻り値
func parseConfig(path string) (config *Config, err error) {
data, err := os.ReadFile(path)
if err != nil {
return // config=nil, err=読み取りエラー
}
config = &Config{}
err = json.Unmarshal(data, config)
return // config=パース結果, err=パースエラーまたはnil
}
// 可変長引数
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
result := sum(1, 2, 3, 4, 5) // 15
// 関数型(第一級関数)
type TransformFunc func(string) string
func apply(s string, fn TransformFunc) string {
return fn(s)
}
result := apply("hello", strings.ToUpper) // "HELLO"
// クロージャ
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
6.2 エラーハンドリング
Goのエラーハンドリングは明示的で、例外(exception)ではなくerror値を使用する:
// errorインターフェース
type error interface {
Error() string
}
// カスタムエラー型
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("バリデーションエラー [%s]: %s", e.Field, e.Message)
}
// エラーラッピング(Go 1.13+)
func readConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("設定ファイルの読み込み失敗 %s: %w", path, err)
}
var config Config
if err := json.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("設定ファイルのパース失敗: %w", err)
}
return &config, nil
}
// エラーの判定
if errors.Is(err, os.ErrNotExist) {
fmt.Println("ファイルが存在しません")
}
var validErr *ValidationError
if errors.As(err, &validErr) {
fmt.Printf("フィールド %s のバリデーションエラー\n", validErr.Field)
}
// 複数エラーの結合(Go 1.20+)
func validateUser(u *User) error {
var errs []error
if u.Name == "" {
errs = append(errs, &ValidationError{Field: "name", Message: "必須項目です"})
}
if u.Email == "" {
errs = append(errs, &ValidationError{Field: "email", Message: "必須項目です"})
}
if len(u.Password) < 8 {
errs = append(errs, &ValidationError{Field: "password", Message: "8文字以上必要です"})
}
return errors.Join(errs...)
}
// sentinel errors(定番エラー)
var (
ErrNotFound = errors.New("リソースが見つかりません")
ErrUnauthorized = errors.New("認証が必要です")
ErrForbidden = errors.New("アクセス権限がありません")
)
7. メソッドとインターフェース
7.1 メソッド
type Rectangle struct {
Width, Height float64
}
// 値レシーバ(構造体のコピーに対して操作)
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
// ポインタレシーバ(構造体を直接変更可能)
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
func (r Rectangle) String() string {
return fmt.Sprintf("Rectangle(%.1f x %.1f)", r.Width, r.Height)
}
rect := Rectangle{Width: 10, Height: 5}
fmt.Println(rect.Area()) // 50
fmt.Println(rect.Perimeter()) // 30
rect.Scale(2)
fmt.Println(rect) // Rectangle(20.0 x 10.0)
レシーバの選択ガイドライン:
- 構造体を変更する場合→ポインタレシーバ
- 構造体が大きい場合→ポインタレシーバ(コピーコスト回避)
- 一貫性のため→型の全メソッドでレシーバを統一
7.2 インターフェース
Goのインターフェースは暗黙的に実装される(implementsキーワード不要):
// インターフェース定義
type Shape interface {
Area() float64
Perimeter() float64
}
type Stringer interface {
String() string
}
// 複数のインターフェースを満たす
// Rectangle は Shape と Stringer の両方を暗黙的に実装
func printShapeInfo(s Shape) {
fmt.Printf("面積: %.2f, 周長: %.2f\n", s.Area(), s.Perimeter())
}
// Circle も Shape を実装
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
// ポリモーフィズム
shapes := []Shape{
Rectangle{Width: 10, Height: 5},
Circle{Radius: 7},
}
for _, s := range shapes {
printShapeInfo(s)
}
// インターフェースの合成
type ReadWriter interface {
io.Reader
io.Writer
}
type ReadWriteCloser interface {
io.Reader
io.Writer
io.Closer
}
// 空インターフェース(any は interface{} のエイリアス)
func printAnything(v any) {
fmt.Printf("型: %T, 値: %v\n", v, v)
}
// 型アサーション
func processValue(v any) {
if str, ok := v.(string); ok {
fmt.Println("文字列:", str)
}
}
7.3 よく使われる標準インターフェース
// io.Reader - データの読み取り
type Reader interface {
Read(p []byte) (n int, err error)
}
// io.Writer - データの書き込み
type Writer interface {
Write(p []byte) (n int, err error)
}
// fmt.Stringer - 文字列表現
type Stringer interface {
String() string
}
// sort.Interface - ソート可能
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
// error - エラー
type error interface {
Error() string
}
// http.Handler - HTTPリクエストハンドラ
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// encoding.json.Marshaler / Unmarshaler
type Marshaler interface {
MarshalJSON() ([]byte, error)
}
type Unmarshaler interface {
UnmarshalJSON([]byte) error
}
// カスタムJSON シリアライゼーションの例
type Timestamp struct {
time.Time
}
func (t Timestamp) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"%s"`, t.Format("2006-01-02T15:04:05Z"))), nil
}
func (t *Timestamp) UnmarshalJSON(data []byte) error {
parsed, err := time.Parse(`"2006-01-02T15:04:05Z"`, string(data))
if err != nil {
return err
}
t.Time = parsed
return nil
}
8. ジェネリクス(Go 1.18+)
8.1 型パラメータの基本
// ジェネリック関数
func Min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
fmt.Println(Min(3, 5)) // 3
fmt.Println(Min(3.14, 2.71)) // 2.71
fmt.Println(Min("a", "b")) // "a"
// 複数の型パラメータ
func Map[T any, U any](slice []T, fn func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = fn(v)
}
return result
}
numbers := []int{1, 2, 3, 4, 5}
strings := Map(numbers, func(n int) string {
return fmt.Sprintf("#%d", n)
})
// strings = ["#1", "#2", "#3", "#4", "#5"]
// Filter関数
func Filter[T any](slice []T, predicate func(T) bool) []T {
var result []T
for _, v := range slice {
if predicate(v) {
result = append(result, v)
}
}
return result
}
evens := Filter(numbers, func(n int) bool { return n%2 == 0 })
// evens = [2, 4]
// Reduce関数
func Reduce[T any, U any](slice []T, initial U, fn func(U, T) U) U {
result := initial
for _, v := range slice {
result = fn(result, v)
}
return result
}
sum := Reduce(numbers, 0, func(acc, n int) int { return acc + n })
// sum = 15
8.2 型制約
// カスタム型制約
type Number interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
~float32 | ~float64
}
func Sum[T Number](numbers []T) T {
var total T
for _, n := range numbers {
total += n
}
return total
}
// チルダ(~)は基底型を含むことを意味する
type Meters float64
type Kilometers float64
distances := []Meters{1.0, 2.5, 3.7}
total := Sum(distances) // Meters型でも動作する
// comparable制約(== と != が使える型)
func Contains[T comparable](slice []T, target T) bool {
for _, v := range slice {
if v == target {
return true
}
}
return false
}
// ジェネリック構造体
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item, true
}
func (s *Stack[T]) Peek() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
return s.items[len(s.items)-1], true
}
// 使用例
intStack := &Stack[int]{}
intStack.Push(1)
intStack.Push(2)
val, _ := intStack.Pop() // 2
stringStack := &Stack[string]{}
stringStack.Push("hello")
stringStack.Push("world")
8.3 ジェネリックインターフェースとパターン
// ジェネリックリポジトリパターン
type Entity interface {
GetID() string
}
type Repository[T Entity] interface {
FindByID(id string) (T, error)
FindAll() ([]T, error)
Create(entity T) error
Update(entity T) error
Delete(id string) error
}
// 具体的な実装
type User struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func (u User) GetID() string { return u.ID }
type InMemoryRepository[T Entity] struct {
data map[string]T
mu sync.RWMutex
}
func NewInMemoryRepository[T Entity]() *InMemoryRepository[T] {
return &InMemoryRepository[T]{
data: make(map[string]T),
}
}
func (r *InMemoryRepository[T]) FindByID(id string) (T, error) {
r.mu.RLock()
defer r.mu.RUnlock()
entity, exists := r.data[id]
if !exists {
var zero T
return zero, fmt.Errorf("エンティティ %s が見つかりません", id)
}
return entity, nil
}
func (r *InMemoryRepository[T]) Create(entity T) error {
r.mu.Lock()
defer r.mu.Unlock()
r.data[entity.GetID()] = entity
return nil
}
// 使用例
userRepo := NewInMemoryRepository[User]()
userRepo.Create(User{ID: "1", Name: "Alice", Email: "alice@example.com"})
9. 並行処理
9.1 Goroutine
Goroutineは軽量なスレッドで、Goランタイムによって管理される。初期スタックサイズはわずか数KBで、必要に応じて動的に拡張される:
// Goroutineの起動
go func() {
fmt.Println("並行実行中")
}()
// 関数をGoroutineとして起動
go processData(data)
// WaitGroupで複数のGoroutineの完了を待つ
func processItems(items []Item) {
var wg sync.WaitGroup
for _, item := range items {
wg.Add(1)
go func(item Item) {
defer wg.Done()
process(item)
}(item)
}
wg.Wait() // 全Goroutineの完了を待つ
}
9.2 チャネル
チャネルはGoroutine間の通信と同期のための仕組み:
// バッファなしチャネル(同期的)
ch := make(chan int)
go func() {
ch <- 42 // 送信(受信側が準備できるまでブロック)
}()
value := <-ch // 受信(送信側が送るまでブロック)
// バッファ付きチャネル(非同期的)
buffered := make(chan string, 10) // バッファサイズ10
buffered <- "hello" // バッファに空きがあればブロックしない
msg := <-buffered
// 方向付きチャネル(型安全性の向上)
func producer(out chan<- int) { // 送信専用
for i := 0; i < 10; i++ {
out <- i
}
close(out)
}
func consumer(in <-chan int) { // 受信専用
for value := range in {
fmt.Println(value)
}
}
ch := make(chan int)
go producer(ch)
consumer(ch)
9.3 並行処理パターン
// === Fan-Out / Fan-In パターン ===
func fanOut(input <-chan int, workers int) []<-chan int {
channels := make([]<-chan int, workers)
for i := 0; i < workers; i++ {
channels[i] = worker(input)
}
return channels
}
func worker(input <-chan int) <-chan int {
output := make(chan int)
go func() {
defer close(output)
for n := range input {
output <- n * n // 重い計算
}
}()
return output
}
func fanIn(channels ...<-chan int) <-chan int {
var wg sync.WaitGroup
merged := make(chan int)
for _, ch := range channels {
wg.Add(1)
go func(c <-chan int) {
defer wg.Done()
for v := range c {
merged <- v
}
}(ch)
}
go func() {
wg.Wait()
close(merged)
}()
return merged
}
// === Pipeline パターン ===
func generate(nums ...int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for _, n := range nums {
out <- n
}
}()
return out
}
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for n := range in {
out <- n * n
}
}()
return out
}
func filter(in <-chan int, predicate func(int) bool) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for n := range in {
if predicate(n) {
out <- n
}
}
}()
return out
}
// パイプライン実行
nums := generate(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
squared := square(nums)
evens := filter(squared, func(n int) bool { return n%2 == 0 })
for result := range evens {
fmt.Println(result) // 4, 16, 36, 64, 100
}
// === Select文(複数チャネルの多重化) ===
func multiplexer(ch1, ch2 <-chan string, done <-chan struct{}) {
for {
select {
case msg := <-ch1:
fmt.Println("チャネル1:", msg)
case msg := <-ch2:
fmt.Println("チャネル2:", msg)
case <-done:
fmt.Println("終了")
return
case <-time.After(5 * time.Second):
fmt.Println("タイムアウト")
return
}
}
}
// === Worker Pool パターン ===
type Job struct {
ID int
Data string
}
type Result struct {
JobID int
Output string
Err error
}
func workerPool(jobs <-chan Job, results chan<- Result, numWorkers int) {
var wg sync.WaitGroup
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
for job := range jobs {
output, err := processJob(job)
results <- Result{
JobID: job.ID,
Output: output,
Err: err,
}
}
}(i)
}
go func() {
wg.Wait()
close(results)
}()
}
// 使用例
jobs := make(chan Job, 100)
results := make(chan Result, 100)
workerPool(jobs, results, 5) // 5つのワーカー
// ジョブの投入
for i := 0; i < 50; i++ {
jobs <- Job{ID: i, Data: fmt.Sprintf("task-%d", i)}
}
close(jobs)
// 結果の収集
for result := range results {
if result.Err != nil {
log.Printf("ジョブ %d 失敗: %v", result.JobID, result.Err)
} else {
log.Printf("ジョブ %d 完了: %s", result.JobID, result.Output)
}
}
// === Context によるキャンセル伝播 ===
func fetchWithTimeout(ctx context.Context, url string) ([]byte, error) {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
// Contextの階層構造
func handleRequest(w http.ResponseWriter, r *http.Request) {
// リクエストコンテキストをベースにする
ctx := r.Context()
// タイムアウト付きのサブコンテキスト
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
// 値を付与したサブコンテキスト
ctx = context.WithValue(ctx, "requestID", uuid.New().String())
result, err := queryDatabase(ctx)
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
http.Error(w, "リクエストタイムアウト", http.StatusGatewayTimeout)
return
}
http.Error(w, "内部エラー", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(result)
}
9.4 同期プリミティブ
// sync.Mutex(排他ロック)
type SafeCounter struct {
mu sync.Mutex
v map[string]int
}
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
defer c.mu.Unlock()
c.v[key]++
}
func (c *SafeCounter) Value(key string) int {
c.mu.Lock()
defer c.mu.Unlock()
return c.v[key]
}
// sync.RWMutex(読み書きロック)
type Cache struct {
mu sync.RWMutex
data map[string]any
}
func (c *Cache) Get(key string) (any, bool) {
c.mu.RLock() // 読み取りロック(複数同時OK)
defer c.mu.RUnlock()
val, ok := c.data[key]
return val, ok
}
func (c *Cache) Set(key string, value any) {
c.mu.Lock() // 書き込みロック(排他的)
defer c.mu.Unlock()
c.data[key] = value
}
// sync.Once(一度だけ実行)
var (
instance *Database
once sync.Once
)
func GetDB() *Database {
once.Do(func() {
instance = &Database{}
instance.Connect("postgres://localhost:5432/mydb")
})
return instance
}
// sync.Pool(オブジェクトプール)
var bufferPool = sync.Pool{
New: func() any {
return new(bytes.Buffer)
},
}
func processRequest(data []byte) string {
buf := bufferPool.Get().(*bytes.Buffer)
defer func() {
buf.Reset()
bufferPool.Put(buf)
}()
buf.Write(data)
return buf.String()
}
// sync.Map(並行安全なマップ)
var cache sync.Map
cache.Store("key1", "value1")
value, ok := cache.Load("key1")
cache.Delete("key1")
// atomic パッケージ
var counter atomic.Int64
counter.Add(1)
counter.Add(5)
fmt.Println(counter.Load()) // 6
10. 標準ライブラリ
10.1 主要パッケージ一覧
Goの標準ライブラリは非常に充実しており、外部依存なしで多くの機能を実現できる:
| パッケージ | 機能 |
|---|---|
fmt | フォーマット付きI/O |
strings | 文字列操作 |
strconv | 文字列変換 |
os | OS機能(ファイル、環境変数等) |
io | I/Oプリミティブ |
bufio | バッファ付きI/O |
net/http | HTTP クライアント/サーバー |
encoding/json | JSON エンコード/デコード |
database/sql | SQL データベースインターフェース |
sync | 同期プリミティブ |
context | キャンセル/タイムアウト伝播 |
testing | テストフレームワーク |
log/slog | 構造化ログ(Go 1.21+) |
crypto/* | 暗号化(AES, RSA, SHA等) |
html/template | HTMLテンプレート |
regexp | 正規表現 |
time | 時間操作 |
path/filepath | ファイルパス操作 |
sort | ソートアルゴリズム |
math | 数学関数 |
reflect | リフレクション |
embed | ファイル埋め込み(Go 1.16+) |
maps | マップ操作(Go 1.21+) |
slices | スライス操作(Go 1.21+) |
iter | イテレータ(Go 1.23+) |
10.2 net/http - HTTPサーバーとクライアント
// === 基本的なHTTPサーバー ===
package main
import (
"encoding/json"
"log"
"net/http"
"time"
)
type APIResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data any `json:"data,omitempty"`
}
func main() {
mux := http.NewServeMux()
// Go 1.22+のパターンマッチング
mux.HandleFunc("GET /api/health", healthHandler)
mux.HandleFunc("GET /api/users/{id}", getUserHandler)
mux.HandleFunc("POST /api/users", createUserHandler)
mux.HandleFunc("PUT /api/users/{id}", updateUserHandler)
mux.HandleFunc("DELETE /api/users/{id}", deleteUserHandler)
// ミドルウェアの適用
handler := loggingMiddleware(corsMiddleware(mux))
server := &http.Server{
Addr: ":8080",
Handler: handler,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
log.Println("サーバー起動: http://localhost:8080")
log.Fatal(server.ListenAndServe())
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, APIResponse{
Status: "ok",
Message: "サーバーは正常に動作しています",
})
}
func getUserHandler(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id") // Go 1.22+
user, err := findUser(id)
if err != nil {
writeJSON(w, http.StatusNotFound, APIResponse{
Status: "error",
Message: "ユーザーが見つかりません",
})
return
}
writeJSON(w, http.StatusOK, APIResponse{
Status: "ok",
Data: user,
})
}
func writeJSON(w http.ResponseWriter, status int, data any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
// ミドルウェア
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start))
})
}
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
// === HTTPクライアント ===
func fetchData(ctx context.Context, url string) (*APIResponse, error) {
client := &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
},
}
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("リクエスト作成失敗: %w", err)
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("リクエスト送信失敗: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, body)
}
var result APIResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("レスポンスパース失敗: %w", err)
}
return &result, nil
}
10.3 encoding/json
// 構造体タグによるJSON制御
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
Description string `json:"description,omitempty"` // 空なら省略
InStock bool `json:"in_stock"`
Tags []string `json:"tags,omitempty"`
Internal string `json:"-"` // JSONから除外
CreatedAt time.Time `json:"created_at"`
}
// マーシャル(Go構造体 → JSON)
product := Product{
ID: 1,
Name: "Go Book",
Price: 3980,
}
jsonBytes, err := json.Marshal(product)
jsonPretty, err := json.MarshalIndent(product, "", " ")
// アンマーシャル(JSON → Go構造体)
var p Product
err := json.Unmarshal(jsonBytes, &p)
// ストリームベースのエンコード/デコード
func handlePost(w http.ResponseWriter, r *http.Request) {
var input CreateRequest
// デコーダで直接読む(メモリ効率が良い)
decoder := json.NewDecoder(r.Body)
decoder.DisallowUnknownFields() // 未知フィールドをエラーにする
if err := decoder.Decode(&input); err != nil {
http.Error(w, "不正なJSON", http.StatusBadRequest)
return
}
// 処理...
// エンコーダで直接書く
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// 動的JSON(構造が不明な場合)
var raw map[string]any
json.Unmarshal(data, &raw)
// json.RawMessage(遅延パース)
type Event struct {
Type string `json:"type"`
Payload json.RawMessage `json:"payload"`
}
var event Event
json.Unmarshal(data, &event)
switch event.Type {
case "user_created":
var payload UserCreatedPayload
json.Unmarshal(event.Payload, &payload)
case "order_placed":
var payload OrderPlacedPayload
json.Unmarshal(event.Payload, &payload)
}
10.4 log/slog - 構造化ログ(Go 1.21+)
import "log/slog"
// デフォルトのテキストハンドラ
slog.Info("サーバー起動", "port", 8080)
// 出力: 2024/01/01 12:00:00 INFO サーバー起動 port=8080
// JSONハンドラ
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug,
}))
slog.SetDefault(logger)
slog.Info("リクエスト処理",
"method", r.Method,
"path", r.URL.Path,
"status", 200,
"duration", time.Since(start),
)
// 出力: {"time":"2024-01-01T12:00:00Z","level":"INFO","msg":"リクエスト処理",
// "method":"GET","path":"/api/users","status":200,"duration":"1.234ms"}
// ロガーのグループ化
logger = logger.With(
slog.String("service", "api"),
slog.String("version", "1.0.0"),
)
// ログレベル
slog.Debug("デバッグ情報")
slog.Info("情報")
slog.Warn("警告")
slog.Error("エラー", "err", err)
// 構造化されたグループ
slog.Info("ユーザーアクション",
slog.Group("user",
slog.String("id", "123"),
slog.String("name", "Alice"),
),
slog.Group("action",
slog.String("type", "login"),
slog.String("ip", "192.168.1.1"),
),
)
11. テストとベンチマーク
11.1 ユニットテスト
// math.go
package math
func Add(a, b int) int {
return a + b
}
func Divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("ゼロ除算")
}
return a / b, nil
}
// math_test.go
package math
import (
"testing"
)
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d, want 5", result)
}
}
// テーブル駆動テスト(Goの推奨パターン)
func TestDivide(t *testing.T) {
tests := []struct {
name string
a, b float64
want float64
wantErr bool
}{
{name: "正常な除算", a: 10, b: 2, want: 5, wantErr: false},
{name: "小数の結果", a: 7, b: 3, want: 2.3333333333333335, wantErr: false},
{name: "ゼロ除算", a: 10, b: 0, want: 0, wantErr: true},
{name: "負の数", a: -10, b: 2, want: -5, wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Divide(tt.a, tt.b)
if (err != nil) != tt.wantErr {
t.Errorf("Divide() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("Divide() = %v, want %v", got, tt.want)
}
})
}
}
// サブテスト
func TestUserService(t *testing.T) {
service := NewUserService(mockDB)
t.Run("Create", func(t *testing.T) {
user, err := service.Create("Alice", "alice@example.com")
if err != nil {
t.Fatalf("ユーザー作成失敗: %v", err)
}
if user.Name != "Alice" {
t.Errorf("Name = %s, want Alice", user.Name)
}
})
t.Run("FindByID", func(t *testing.T) {
user, err := service.FindByID("1")
if err != nil {
t.Fatalf("ユーザー検索失敗: %v", err)
}
if user == nil {
t.Fatal("ユーザーがnil")
}
})
}
// TestMain(テストのセットアップ/クリーンアップ)
func TestMain(m *testing.M) {
// セットアップ
db := setupTestDB()
// テスト実行
code := m.Run()
// クリーンアップ
db.Close()
os.Exit(code)
}
11.2 ベンチマーク
// benchmark_test.go
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(100, 200)
}
}
// メモリアロケーションのベンチマーク
func BenchmarkStringConcat(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
s := ""
for j := 0; j < 100; j++ {
s += "a"
}
}
}
func BenchmarkStringBuilder(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
var sb strings.Builder
for j := 0; j < 100; j++ {
sb.WriteString("a")
}
_ = sb.String()
}
}
// サブベンチマーク
func BenchmarkSort(b *testing.B) {
sizes := []int{100, 1000, 10000}
for _, size := range sizes {
b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
data := make([]int, size)
for i := range data {
data[i] = rand.Intn(size)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
slices.Sort(slices.Clone(data))
}
})
}
}
11.3 テストの実行
# 全テストの実行
go test ./...
# 特定パッケージのテスト
go test ./internal/auth/
# 特定のテスト関数
go test -run TestDivide ./math/
# 詳細出力
go test -v ./...
# カバレッジ
go test -cover ./...
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out # ブラウザで表示
go tool cover -func=coverage.out # 関数ごとのカバレッジ
# ベンチマーク
go test -bench=. ./...
go test -bench=BenchmarkSort -benchmem ./...
go test -bench=. -count=5 ./... # 5回実行して安定性確認
# レースディテクタ
go test -race ./...
# ファジング(Go 1.18+)
go test -fuzz=FuzzParseJSON ./...
# タイムアウト設定
go test -timeout 60s ./...
# キャッシュ無効化
go test -count=1 ./...
11.4 ファジング(Go 1.18+)
// fuzz_test.go
func FuzzParseJSON(f *testing.F) {
// シードコーパスの追加
f.Add(`{"name": "Alice", "age": 30}`)
f.Add(`{"name": "", "age": 0}`)
f.Add(`{}`)
f.Add(`[]`)
f.Fuzz(func(t *testing.T, input string) {
var result map[string]any
err := json.Unmarshal([]byte(input), &result)
if err != nil {
return // パースエラーは想定内
}
// パースできたなら再マーシャルも成功するべき
_, err = json.Marshal(result)
if err != nil {
t.Errorf("Marshal失敗: %v (input: %s)", err, input)
}
})
}
12. データベースアクセス
12.1 database/sql パッケージ
import (
"database/sql"
_ "github.com/lib/pq" // PostgreSQL ドライバ
// _ "github.com/go-sql-driver/mysql" // MySQL ドライバ
)
// 接続プールの設定
func NewDB(dsn string) (*sql.DB, error) {
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, fmt.Errorf("DB接続失敗: %w", err)
}
// 接続プールの設定
db.SetMaxOpenConns(25) // 最大同時接続数
db.SetMaxIdleConns(10) // 最大アイドル接続数
db.SetConnMaxLifetime(5 * time.Minute) // 接続の最大寿命
db.SetConnMaxIdleTime(1 * time.Minute) // アイドル接続の最大寿命
// 接続確認
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
return nil, fmt.Errorf("DB ping失敗: %w", err)
}
return db, nil
}
// CRUD操作
type UserRepository struct {
db *sql.DB
}
// INSERT
func (r *UserRepository) Create(ctx context.Context, user *User) error {
query := `
INSERT INTO users (name, email, created_at)
VALUES ($1, $2, $3)
RETURNING id`
return r.db.QueryRowContext(ctx, query,
user.Name, user.Email, time.Now(),
).Scan(&user.ID)
}
// SELECT (単一行)
func (r *UserRepository) FindByID(ctx context.Context, id int) (*User, error) {
query := `SELECT id, name, email, created_at FROM users WHERE id = $1`
user := &User{}
err := r.db.QueryRowContext(ctx, query, id).Scan(
&user.ID, &user.Name, &user.Email, &user.CreatedAt,
)
if err == sql.ErrNoRows {
return nil, ErrNotFound
}
if err != nil {
return nil, fmt.Errorf("ユーザー検索失敗: %w", err)
}
return user, nil
}
// SELECT (複数行)
func (r *UserRepository) FindAll(ctx context.Context) ([]*User, error) {
query := `SELECT id, name, email, created_at FROM users ORDER BY id`
rows, err := r.db.QueryContext(ctx, query)
if err != nil {
return nil, fmt.Errorf("ユーザー一覧取得失敗: %w", err)
}
defer rows.Close()
var users []*User
for rows.Next() {
user := &User{}
if err := rows.Scan(&user.ID, &user.Name, &user.Email, &user.CreatedAt); err != nil {
return nil, fmt.Errorf("行スキャン失敗: %w", err)
}
users = append(users, user)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("イテレーションエラー: %w", err)
}
return users, nil
}
// トランザクション
func (r *UserRepository) Transfer(ctx context.Context, fromID, toID int, amount float64) error {
tx, err := r.db.BeginTx(ctx, &sql.TxOptions{
Isolation: sql.LevelSerializable,
})
if err != nil {
return fmt.Errorf("トランザクション開始失敗: %w", err)
}
defer tx.Rollback() // コミット済みならno-op
// 残高確認と更新
var balance float64
err = tx.QueryRowContext(ctx,
`SELECT balance FROM accounts WHERE user_id = $1 FOR UPDATE`, fromID,
).Scan(&balance)
if err != nil {
return fmt.Errorf("残高確認失敗: %w", err)
}
if balance < amount {
return errors.New("残高不足")
}
// 送金元から引き落とし
_, err = tx.ExecContext(ctx,
`UPDATE accounts SET balance = balance - $1 WHERE user_id = $2`,
amount, fromID,
)
if err != nil {
return fmt.Errorf("引き落とし失敗: %w", err)
}
// 送金先に入金
_, err = tx.ExecContext(ctx,
`UPDATE accounts SET balance = balance + $1 WHERE user_id = $2`,
amount, toID,
)
if err != nil {
return fmt.Errorf("入金失敗: %w", err)
}
return tx.Commit()
}
13. Web開発
13.1 Ginフレームワークを使ったREST API
package main
import (
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
)
type Book struct {
ID int `json:"id"`
Title string `json:"title" binding:"required"`
Author string `json:"author" binding:"required"`
ISBN string `json:"isbn" binding:"required,len=13"`
Price float64 `json:"price" binding:"required,gt=0"`
CreatedAt time.Time `json:"created_at"`
}
func main() {
r := gin.Default()
// ミドルウェア
r.Use(gin.Logger())
r.Use(gin.Recovery())
r.Use(rateLimitMiddleware())
// ルートグループ
v1 := r.Group("/api/v1")
{
books := v1.Group("/books")
{
books.GET("", listBooks)
books.GET("/:id", getBook)
books.POST("", createBook)
books.PUT("/:id", updateBook)
books.DELETE("/:id", deleteBook)
}
}
r.Run(":8080")
}
func listBooks(c *gin.Context) {
// クエリパラメータ
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
search := c.Query("search")
books, total, err := bookService.List(c.Request.Context(), page, limit, search)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "取得失敗"})
return
}
c.JSON(http.StatusOK, gin.H{
"data": books,
"total": total,
"page": page,
"limit": limit,
})
}
func createBook(c *gin.Context) {
var book Book
if err := c.ShouldBindJSON(&book); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "バリデーションエラー",
"details": err.Error(),
})
return
}
if err := bookService.Create(c.Request.Context(), &book); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "作成失敗"})
return
}
c.JSON(http.StatusCreated, book)
}
// カスタムミドルウェア
func rateLimitMiddleware() gin.HandlerFunc {
limiter := rate.NewLimiter(rate.Every(time.Second), 100) // 毎秒100リクエスト
return func(c *gin.Context) {
if !limiter.Allow() {
c.JSON(http.StatusTooManyRequests, gin.H{
"error": "レート制限超過",
})
c.Abort()
return
}
c.Next()
}
}
// 認証ミドルウェア
func authMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "認証必須"})
c.Abort()
return
}
claims, err := validateToken(token)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "無効なトークン"})
c.Abort()
return
}
c.Set("userID", claims.UserID)
c.Next()
}
}
13.2 gRPC
// proto定義: user.proto
// syntax = "proto3";
// package user;
// option go_package = "./pb";
//
// service UserService {
// rpc GetUser(GetUserRequest) returns (UserResponse);
// rpc ListUsers(ListUsersRequest) returns (stream UserResponse);
// rpc CreateUser(CreateUserRequest) returns (UserResponse);
// }
// gRPCサーバー実装
type userServer struct {
pb.UnimplementedUserServiceServer
repo UserRepository
}
func (s *userServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.UserResponse, error) {
user, err := s.repo.FindByID(ctx, req.GetId())
if err != nil {
return nil, status.Errorf(codes.NotFound, "ユーザーが見つかりません: %v", err)
}
return &pb.UserResponse{
Id: user.ID,
Name: user.Name,
Email: user.Email,
}, nil
}
func (s *userServer) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
users, err := s.repo.FindAll(stream.Context())
if err != nil {
return status.Errorf(codes.Internal, "取得失敗: %v", err)
}
for _, user := range users {
if err := stream.Send(&pb.UserResponse{
Id: user.ID,
Name: user.Name,
Email: user.Email,
}); err != nil {
return err
}
}
return nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("リッスン失敗: %v", err)
}
grpcServer := grpc.NewServer(
grpc.UnaryInterceptor(loggingInterceptor),
grpc.StreamInterceptor(streamLoggingInterceptor),
)
pb.RegisterUserServiceServer(grpcServer, &userServer{
repo: NewUserRepository(db),
})
// gRPCリフレクション(開発用)
reflection.Register(grpcServer)
log.Println("gRPCサーバー起動: :50051")
log.Fatal(grpcServer.Serve(lis))
}
14. ビルドとクロスコンパイル
14.1 ビルドコマンド
# 基本的なビルド
go build -o myapp ./cmd/api/
# 最適化ビルド(デバッグ情報の削除 → バイナリサイズ削減)
go build -ldflags="-s -w" -o myapp ./cmd/api/
# バージョン情報の埋め込み
go build -ldflags="-X main.version=1.0.0 -X main.buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ) -X main.gitCommit=$(git rev-parse --short HEAD)" -o myapp ./cmd/api/
# ビルドタグ
go build -tags "production,metrics" -o myapp ./cmd/api/
// バージョン情報の参照
package main
var (
version = "dev"
buildTime = "unknown"
gitCommit = "unknown"
)
func main() {
fmt.Printf("Version: %s\nBuild: %s\nCommit: %s\n",
version, buildTime, gitCommit)
}
14.2 クロスコンパイル
# Linux AMD64向け
GOOS=linux GOARCH=amd64 go build -o myapp-linux-amd64 ./cmd/api/
# Linux ARM64向け(AWS Graviton等)
GOOS=linux GOARCH=arm64 go build -o myapp-linux-arm64 ./cmd/api/
# Windows向け
GOOS=windows GOARCH=amd64 go build -o myapp.exe ./cmd/api/
# macOS ARM64向け(Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o myapp-darwin-arm64 ./cmd/api/
# WebAssembly向け
GOOS=js GOARCH=wasm go build -o myapp.wasm ./cmd/wasm/
# サポートされるOS/アーキテクチャの一覧
go tool dist list
14.3 ファイル埋め込み(Go 1.16+)
package main
import (
"embed"
"io/fs"
"net/http"
)
//go:embed static/*
var staticFiles embed.FS
//go:embed templates/*.html
var templateFiles embed.FS
//go:embed config/default.yaml
var defaultConfig []byte
//go:embed version.txt
var version string
func main() {
// 静的ファイルの配信
staticFS, _ := fs.Sub(staticFiles, "static")
http.Handle("/static/", http.StripPrefix("/static/",
http.FileServer(http.FS(staticFS))))
// テンプレートの読み込み
tmpl, _ := template.ParseFS(templateFiles, "templates/*.html")
// 設定ファイルの読み込み
var config Config
yaml.Unmarshal(defaultConfig, &config)
fmt.Println("Version:", version)
}
15. パフォーマンス最適化
15.1 プロファイリング
import (
"net/http"
_ "net/http/pprof" // pprofエンドポイントを自動登録
"runtime"
)
func main() {
// pprofサーバー(別ポートで起動推奨)
go func() {
http.ListenAndServe(":6060", nil)
}()
// アプリケーション起動
runApp()
}
# CPUプロファイル
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
# メモリプロファイル
go tool pprof http://localhost:6060/debug/pprof/heap
# Goroutineプロファイル
go tool pprof http://localhost:6060/debug/pprof/goroutine
# ブロッキングプロファイル
go tool pprof http://localhost:6060/debug/pprof/block
# トレース
curl -o trace.out http://localhost:6060/debug/pprof/trace?seconds=5
go tool trace trace.out
# pprofコマンド
# (pprof) top # 上位消費関数
# (pprof) list funcName # 関数のソースコード表示
# (pprof) web # グラフ表示(ブラウザ)
# (pprof) flame # フレームグラフ
15.2 メモリ最適化
// スライスの事前確保
// ✗ 悪い例(何度もアロケーション)
var results []int
for i := 0; i < 10000; i++ {
results = append(results, i)
}
// ✓ 良い例(一度のアロケーション)
results := make([]int, 0, 10000)
for i := 0; i < 10000; i++ {
results = append(results, i)
}
// 文字列結合の最適化
// ✗ 悪い例(毎回新しい文字列を生成)
s := ""
for i := 0; i < 1000; i++ {
s += "a"
}
// ✓ 良い例(strings.Builder)
var sb strings.Builder
sb.Grow(1000) // 事前確保
for i := 0; i < 1000; i++ {
sb.WriteString("a")
}
result := sb.String()
// sync.Poolの活用
var jsonBufPool = sync.Pool{
New: func() any {
return new(bytes.Buffer)
},
}
// 構造体のフィールド順序(メモリアライメント)
// ✗ パディングが多い
type Bad struct {
a bool // 1 byte + 7 padding
b int64 // 8 bytes
c bool // 1 byte + 3 padding
d int32 // 4 bytes
} // = 24 bytes
// ✓ パディングが少ない
type Good struct {
b int64 // 8 bytes
d int32 // 4 bytes
a bool // 1 byte
c bool // 1 byte + 2 padding
} // = 16 bytes
15.3 Profile-Guided Optimization (PGO)(Go 1.21+)
# 1. 通常ビルド
go build -o myapp ./cmd/api/
# 2. プロファイルの収集(本番環境で)
curl -o default.pgo http://localhost:6060/debug/pprof/profile?seconds=60
# 3. PGOを適用してビルド
# default.pgo をメインパッケージに配置するだけで自動適用
cp default.pgo ./cmd/api/
go build -o myapp ./cmd/api/
# 通常2-7%のパフォーマンス向上が見込める
16. ツールチェインと開発者体験
16.1 Go標準ツール
# フォーマット(全ファイル)
gofmt -w .
go fmt ./...
# コードの静的解析
go vet ./...
# ドキュメントの表示
go doc fmt.Println
go doc -all net/http
# ドキュメントサーバー
godoc -http=:6060
# 依存関係の脆弱性チェック(Go 1.18+)
govulncheck ./...
# コードの自動修正
gofmt -s -w . # 簡略化
# インストール(バイナリのインストール)
go install golang.org/x/tools/gopls@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
16.2 golangci-lint(リンター集約ツール)
# .golangci.yml
run:
timeout: 5m
go: '1.24'
linters:
enable:
- errcheck # エラーチェック漏れ
- govet # go vet
- staticcheck # 高度な静的解析
- unused # 未使用コード
- gosimple # コード簡略化
- ineffassign # 無効な代入
- typecheck # 型チェック
- gocritic # コードレビュー
- revive # golintの後継
- misspell # スペルミス
- gofumpt # 厳密なフォーマット
- gosec # セキュリティ
- prealloc # スライスの事前確保
linters-settings:
gocritic:
enabled-tags:
- diagnostic
- performance
- style
revive:
rules:
- name: exported
severity: warning
- name: unexported-return
severity: warning
issues:
exclude-rules:
- path: _test\.go
linters:
- errcheck
- gosec
# 実行
golangci-lint run ./...
golangci-lint run --fix ./... # 自動修正
16.3 Makefile例
# Makefile
.PHONY: all build test lint clean run docker
APP_NAME := myapp
VERSION := $(shell git describe --tags --always --dirty)
BUILD_TIME := $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
GIT_COMMIT := $(shell git rev-parse --short HEAD)
LDFLAGS := -ldflags "-s -w -X main.version=$(VERSION) -X main.buildTime=$(BUILD_TIME) -X main.gitCommit=$(GIT_COMMIT)"
all: lint test build
build:
CGO_ENABLED=0 go build $(LDFLAGS) -o bin/$(APP_NAME) ./cmd/api/
run:
go run ./cmd/api/
test:
go test -race -cover -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
test-integration:
go test -race -tags=integration -cover ./...
lint:
golangci-lint run ./...
go vet ./...
fmt:
gofmt -s -w .
goimports -w .
clean:
rm -rf bin/ coverage.out
docker:
docker build -t $(APP_NAME):$(VERSION) .
generate:
go generate ./...
vulncheck:
govulncheck ./...
# クロスコンパイル
build-all:
GOOS=linux GOARCH=amd64 go build $(LDFLAGS) -o bin/$(APP_NAME)-linux-amd64 ./cmd/api/
GOOS=linux GOARCH=arm64 go build $(LDFLAGS) -o bin/$(APP_NAME)-linux-arm64 ./cmd/api/
GOOS=darwin GOARCH=arm64 go build $(LDFLAGS) -o bin/$(APP_NAME)-darwin-arm64 ./cmd/api/
GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o bin/$(APP_NAME)-windows-amd64.exe ./cmd/api/
16.4 Dockerfile(マルチステージビルド)
# ビルドステージ
FROM golang:1.24-alpine AS builder
RUN apk add --no-cache git ca-certificates
WORKDIR /app
# 依存関係のキャッシュ
COPY go.mod go.sum ./
RUN go mod download
# ソースコードのコピーとビルド
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
-ldflags="-s -w" \
-o /app/bin/server \
./cmd/api/
# 実行ステージ
FROM scratch
# SSL証明書(HTTPS通信に必要)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# バイナリのコピー
COPY --from=builder /app/bin/server /server
# 非rootユーザーで実行
USER 65534:65534
EXPOSE 8080
ENTRYPOINT ["/server"]
17. 設計パターンとベストプラクティス
17.1 依存性注入
// インターフェースによる抽象化
type UserRepository interface {
FindByID(ctx context.Context, id string) (*User, error)
Create(ctx context.Context, user *User) error
}
type EmailService interface {
Send(ctx context.Context, to, subject, body string) error
}
// サービス層(依存関係はインターフェースで受け取る)
type UserService struct {
repo UserRepository
email EmailService
log *slog.Logger
}
func NewUserService(repo UserRepository, email EmailService, log *slog.Logger) *UserService {
return &UserService{
repo: repo,
email: email,
log: log,
}
}
func (s *UserService) Register(ctx context.Context, name, emailAddr string) (*User, error) {
user := &User{
ID: uuid.New().String(),
Name: name,
Email: emailAddr,
}
if err := s.repo.Create(ctx, user); err != nil {
return nil, fmt.Errorf("ユーザー作成失敗: %w", err)
}
// ウェルカムメール送信(エラーはログのみ)
if err := s.email.Send(ctx, emailAddr, "ようこそ", "登録完了"); err != nil {
s.log.Error("メール送信失敗", "err", err, "email", emailAddr)
}
return user, nil
}
// メイン関数での組み立て
func main() {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
db, _ := sql.Open("postgres", os.Getenv("DATABASE_URL"))
userRepo := postgres.NewUserRepository(db)
emailSvc := smtp.NewEmailService(os.Getenv("SMTP_HOST"))
userService := NewUserService(userRepo, emailSvc, logger)
// HTTPハンドラーの設定
handler := api.NewHandler(userService)
http.ListenAndServe(":8080", handler)
}
17.2 オプションパターン(Functional Options)
type Server struct {
host string
port int
readTimeout time.Duration
writeTimeout time.Duration
maxConns int
logger *slog.Logger
}
type Option func(*Server)
func WithHost(host string) Option {
return func(s *Server) { s.host = host }
}
func WithPort(port int) Option {
return func(s *Server) { s.port = port }
}
func WithReadTimeout(d time.Duration) Option {
return func(s *Server) { s.readTimeout = d }
}
func WithWriteTimeout(d time.Duration) Option {
return func(s *Server) { s.writeTimeout = d }
}
func WithMaxConns(n int) Option {
return func(s *Server) { s.maxConns = n }
}
func WithLogger(l *slog.Logger) Option {
return func(s *Server) { s.logger = l }
}
func NewServer(opts ...Option) *Server {
// デフォルト値
s := &Server{
host: "0.0.0.0",
port: 8080,
readTimeout: 15 * time.Second,
writeTimeout: 15 * time.Second,
maxConns: 100,
logger: slog.Default(),
}
// オプションの適用
for _, opt := range opts {
opt(s)
}
return s
}
// 使用例
server := NewServer(
WithPort(9090),
WithReadTimeout(30*time.Second),
WithMaxConns(200),
)
17.3 エラーハンドリングのベストプラクティス
// 1. エラーにはコンテキストを付与する
func (s *UserService) GetUser(ctx context.Context, id string) (*User, error) {
user, err := s.repo.FindByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("ユーザー取得 (id=%s): %w", id, err)
}
return user, nil
}
// 2. Sentinel Errors は公開APIの境界で定義
var (
ErrNotFound = errors.New("not found")
ErrConflict = errors.New("conflict")
ErrUnauthorized = errors.New("unauthorized")
)
// 3. errors.Is / errors.As で判定
func handleError(w http.ResponseWriter, err error) {
switch {
case errors.Is(err, ErrNotFound):
http.Error(w, "Not Found", http.StatusNotFound)
case errors.Is(err, ErrUnauthorized):
http.Error(w, "Unauthorized", http.StatusUnauthorized)
case errors.Is(err, ErrConflict):
http.Error(w, "Conflict", http.StatusConflict)
default:
slog.Error("内部エラー", "err", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}
// 4. panic は本当に回復不可能な場合のみ
func mustParseTemplate(name string) *template.Template {
t, err := template.ParseFiles(name)
if err != nil {
panic(fmt.Sprintf("テンプレートパース失敗: %v", err))
}
return t
}
18. エコシステムとコミュニティ
18.1 主要なサードパーティライブラリ
| カテゴリ | ライブラリ | 説明 |
|---|---|---|
| Web Framework | gin, echo, fiber, chi | HTTPルーティングとミドルウェア |
| ORM | gorm, ent, sqlc, sqlx | データベース操作 |
| 認証 | golang-jwt, oauth2 | JWT, OAuth2 |
| バリデーション | validator | 構造体バリデーション |
| 設定管理 | viper, envconfig | 設定ファイル/環境変数 |
| CLI | cobra, urfave/cli | コマンドラインツール |
| ログ | zap, zerolog, slog | 構造化ログ |
| テスト | testify, gomock, mockgen | テストユーティリティ |
| gRPC | grpc-go, protobuf | RPC通信 |
| メッセージキュー | sarama (Kafka), amqp (RabbitMQ) | 非同期メッセージング |
| Redis | go-redis, redigo | Redisクライアント |
| HTTP Client | resty | REST APIクライアント |
| 監視 | prometheus/client_golang | メトリクス |
| トレーシング | opentelemetry-go | 分散トレーシング |
18.2 開発リソース
- 公式サイト: https://go.dev/
- 公式ブログ: https://go.dev/blog/
- パッケージ検索: https://pkg.go.dev/
- A Tour of Go: https://go.dev/tour/
- Effective Go: https://go.dev/doc/effective_go
- Go Playground: https://go.dev/play/
- Go Wiki: https://go.dev/wiki/
- Go by Example: https://gobyexample.com/
18.3 Go 1.24+ の最新機能と今後のロードマップ
Go 1.24(2025年2月リリース)の主な新機能:
- ジェネリクスの型推論強化: インターフェースメソッドの型パラメータの推論が改善
- テストの改善:
testing.B.Loopメソッドの追加 - 標準ライブラリ:
os.Root型の追加、weakパッケージの追加 - ツールチェイン:
go toolでのモジュールベースのツール管理
今後期待される機能:
- イテレータの成熟:
iterパッケージのさらなる発展 - 構造化並行処理: Goroutineの構造化されたライフサイクル管理
- Sum Types / 列挙型: ディスクリミネーテッドユニオン(議論中)
19. まとめ
Go言語は、シンプルさ、パフォーマンス、並行処理の3つの柱を中心に設計された実用的なプログラミング言語である。以下にGoの選択が特に適しているケースをまとめる:
Goを選ぶべき場面:
- 高スループットなWebサービス/APIの構築
- マイクロサービスアーキテクチャの実装
- CLIツールの開発
- クラウドネイティブなインフラツール
- ネットワーク集約型のアプリケーション
- チーム開発でコードの一貫性を重視する場合
他の言語を検討すべき場面:
- GUIアプリケーション(→ Swift, Kotlin, C#)
- 機械学習/データサイエンス(→ Python)
- リアルタイムシステム(GCの影響を排除したい → Rust, C)
- 複雑なドメインモデリング(→ Scala, Haskell)
Goは「退屈な技術(Boring Technology)」を自認しており、それこそがGoの最大の強みである。予測可能で、読みやすく、メンテナンスしやすいコードを書くことを可能にし、チームの生産性を最大化する。
クラウドネイティブ時代において、Docker、Kubernetes、Terraformなどの主要インフラツールがGoで書かれている事実は、Goがこの分野における事実上の標準言語であることを示している。今後もGoは、シンプルさを維持しながら、現代的なソフトウェア開発の課題に対応し続けるだろう。