Go チートシート
Go 1.27 をベースにしたチートシートです。
package main // 実行可能バイナリは必ず package main
import ( "fmt" "os")
func main() { fmt.Println("Hello, Go 1.27") os.Exit(0)}- ファイルの先頭に必ず
package宣言 importはグループ化推奨(標準ライブラリ → 外部パッケージの順)main()がエントリポイント
変数・定数・型
Section titled “変数・定数・型”// varvar name = "Gopher" // string(初期値から型を推論)var age int // 初期値なしでは型の指定が必要。ゼロ値 = 0var id int64 = 42 // 推論される int ではなく int64 にしたい場合は明示
// 短縮宣言(関数内のみ)city := "Tokyo"
// 複数同時宣言var ( x, y int = 1, 2 msg string)初期値があれば var name = "Gopher" のように型推論が利用できます。
初期値がない場合は var age int のように型指定が必要です。
:= は関数内でのみ使え、左辺に少なくとも1つの新しい変数が必要です。
| 型の分類 | 型 | ゼロ値 |
|---|---|---|
| 数値 | 整数・浮動小数点・複素数 | 0(複素数は 0 + 0i) |
| 文字列 | string |
"" |
| 真偽値 | bool |
false |
| 配列 | [N]T |
すべての要素が T のゼロ値 |
| 構造体 | struct |
すべてのフィールドが各型のゼロ値 |
| 参照を含む型 | pointer, slice, map, chan, func, interface | nil |
| 定義型 | type UserID int など |
基底型のゼロ値 |
| 分類 | 型 | 補足 |
|---|---|---|
| 真偽値 | bool |
true または false |
| 文字列 | string |
不変なバイト列 |
| 符号付き整数 | int, int8, int16, int32, int64 |
int のビット幅は実装依存 |
| 符号なし整数 | uint, uint8, uint16, uint32, uint64, uintptr |
uintptr はポインタのビット列を保持できる整数型 |
| エイリアス | byte, rune |
それぞれ uint8, int32 のエイリアス |
| 浮動小数点 | float32, float64 |
IEEE 754 |
| 複素数 | complex64, complex128 |
実部・虚部がそれぞれ float32 / float64 |
// 整数var i8 int8 = 127 // -128 〜 127var i64 int64 = 9223372036854775807var u uint = 42 // 符号なしvar ptr uintptr // ポインタ幅
// 浮動小数点var f32 float32 = 3.14var f64 float64 = 3.141592653589793
// 複素数var c complex128 = 1 + 2i
// 文字列(不変なバイト列。通常は UTF-8 テキストとして扱うが、不正 UTF-8 も保持可能)s := "日本語OK"r := []rune(s) // Unicode コードポイントで扱うb := []byte(s) // バイト列で扱う
// rune = int32 のエイリアス(Unicode コードポイント)var ch rune = '🐹'i := 42f := float64(i) // 必ず明示して変換するs := fmt.Sprintf("%d", i) // 数値→文字列n, _ := strconv.Atoi("123") // 文字列→数値const Pi = 3.14159
// iota:連番定数type Weekday intconst ( Sunday Weekday = iota // 0 Monday // 1 Tuesday // 2)
// ビットフラグパターンtype Permission uintconst ( Read Permission = 1 << iota // 1 Write // 2 Execute // 4)x := 10p := &x // アドレス取得*p = 20 // デリファレンスfmt.Println(x) // 20
// new でゼロ値ポインタ生成p2 := new(int) // *int、値は 0
// Go 1.26 から:式で初期値を指定できる(Go 1.25 以前では不可)p3 := new(42) // *int、値は 42// 初期化ステートメント付きif err := doSomething(); err != nil { fmt.Println("error:", err)}
// 通常 if-elseif 0 < x { fmt.Println("positive")} else if x < 0 { fmt.Println("negative")} else { fmt.Println("zero")}for (Go に while はない)
Section titled “for (Go に while はない)”// C スタイルfor i := 0; i < 5; i++ { }
// while 相当for 0 < n { n-- }
// 無限ループfor { if done { break }}
// range:インデックス+値for i, v := range []int{10, 20, 30} { fmt.Println(i, v) // 0 10 / 1 20 / 2 30}
// インデックスだけfor i := range slice { }
// 値だけ(_ で捨てる)for _, v := range slice { }
// マップfor k, v := range m { }
// 文字列は rune でイテレートfor i, r := range "日本語" { fmt.Printf("%d: %c\n", i, r)}// 0: 日// 3: 本// 6: 語switch
Section titled “switch”// 式 switch(fallthrough は明示が必要)switch os := runtime.GOOS; os {case "darwin": fmt.Println("macOS")case "linux": fmt.Println("Linux")default: fmt.Println("Other")}
// 条件なし switch(if-else チェーンの代替)switch {case 100 < x: fmt.Println("big")case 10 < x: fmt.Println("medium")default: fmt.Println("small")}
// type switch(インターフェース判別)func describe(i any) { switch v := i.(type) { case int: fmt.Printf("int: %d\n", v) case string: fmt.Printf("string: %q\n", v) default: fmt.Printf("unknown: %T\n", v) }}defer は「その関数が終了する直前に実行する処理を予約する」ための仕組みです。
主にファイルやレスポンスボディ、ロックの解放等に使用します。
func writeFile(path string) error { f, err := os.Create(path) if err != nil { return err } defer f.Close() // 関数終了時に必ず実行
// defer は LIFO 順 defer fmt.Println("3rd") defer fmt.Println("2nd") defer fmt.Println("1st") // → "1st", "2nd", "3rd" の順に出力
return nil}defer は引数をその時点で評価します。
ループ内で使う場合、defer は関数が終わるまで実行されない点に注意が必要です。
ループごとにファイルを開く処理などは、defer に頼らず即クローズするか、処理を別関数に切り出してください。
// 複数戻り値func divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("division by zero") } return a / b, nil}
result, err := divide(10, 3)
// 名前付き戻り値(ドキュメント目的・短い関数向け)func minMax(nums []int) (min, max int) { min, max = nums[0], nums[0] for _, n := range nums[1:] { if n < min { min = n } if n > max { max = n } } return // naked return}func sum(nums ...int) int { total := 0 for _, n := range nums { total += n } return total}
sum(1, 2, 3)nums := []int{1, 2, 3}sum(nums...) // スライスを展開第一級関数・クロージャ
Section titled “第一級関数・クロージャ”Go では関数を値として扱えます。変数への代入、引数としての受け渡し、戻り値としての返却ができる性質を第一級関数と呼びます。
クロージャは、関数が定義された外側の変数を参照・保持できる関数値です。外側の関数が終了した後も、キャプチャした変数の状態を保持できます。
// 関数を変数に代入add := func(a, b int) int { return a + b }
// 関数を引数に渡すapply := func(f func(int, int) int, x, y int) int { return f(x, y)}apply(add, 3, 4) // 7
// クロージャ:外側の変数をキャプチャfunc counter() func() int { n := 0 return func() int { n++ return n }}c := counter()c() // 1c() // 2init 関数
Section titled “init 関数”init はパッケージの初期化時に自動で呼ばれる特別な関数です。
main 関数より前に実行され、1つのパッケージ内に複数定義できます。
通常のアプリケーション初期化は main や明示的な初期化関数で行う方が、依存関係とエラー処理を追いやすくなります。
init はパッケージ内部の登録処理など、実行順序を意識せずに済む用途に限定するのが無難です。
// パッケージ初期化時に自動実行(複数定義可)func init() { // ドライバやハンドラの登録など}配列・スライス・マップ
Section titled “配列・スライス・マップ”メモリレイアウト概念図
Section titled “メモリレイアウト概念図”配列(固定長・値型)┌───┬───┬───┬───┬───┐│ 0 │ 1 │ 2 │ 3 │ 4 │ ← 連続したメモリ領域(配置はコンパイラが決定)└───┴───┴───┴───┴───┘
スライス(可変長ビュー)┌────────┬──────┬──────┐│ ptr │ len │ cap │ ← スライスヘッダ(3ワード)└────┬───┴──────┴──────┘ ↓┌───┬───┬───┬───┬───┬───┐│ 0 │ 1 │ 2 │ 3 │ │ │ ← 別の場所に確保された配列└───┴───┴───┴───┴───┴───┘ ↑ ↑ len=4 cap=6
※ 配列・スライスの実際の配置(スタック/ヒープ)はエスケープ解析で決まります配列は長さが型の一部である「固定長」のコレクションです。[3]int と [4]int は別の型になり、代入や関数引数への受け渡しでは要素ごとコピーされます。要素数が固定の値を扱う場合に使います。
var a [3]int // [0 0 0]b := [3]int{1, 2, 3}c := [...]int{4, 5, 6} // 長さ自動推論
// 配列は値型(コピーされる)d := bd[0] = 99 // b は変わらないスライスは配列の一部または全体を参照する、可変長のビューです。
配列との主な違いは、長さが型の一部ではないこと、代入しても要素全体はコピーされず基底配列を共有し得ること、append で長さを増やせます。
// 生成s := []int{1, 2, 3}s2 := make([]int, 5) // len=5, cap=5s3 := make([]int, 3, 10) // len=3, cap=10
// 操作s = append(s, 4, 5) // cap 超えると新配列にコピーs = append(s, []int{6, 7}...)
// スライシング(同じ配列を参照)sub := s[1:3] // s[1], s[2](元配列の参照)sub2 := s[1:3:3] // 第3引数でキャップを制限
// コピー(独立したメモリ)dst := make([]int, len(s))copy(dst, s)
// 削除(順序維持)i := 2s = append(s[:i], s[i+1:]...)
// 長さ・容量fmt.Println(len(s), cap(s))sub := s[1:3] のようにスライシングすると、sub と s は同じ配列を指します。
sub を変更すると s にも影響します。独立させるには copy を使いましょう。
マップはキーと値を対応付けて保持するコレクションです。 要素の順序は保証されず、キーには比較可能な型(数値、文字列、ポインタ、配列、比較可能な構造体など)を使えます。 slice、map、func はキーには使えません。
// 生成m := map[string]int{"a": 1, "b": 2}m2 := make(map[string]int)
// 操作m["c"] = 3delete(m, "a")
// 存在確認v, ok := m["x"] // ok=false なら存在しないif ok { fmt.Println(v)}
// イテレート(順序は不定)for k, v := range m { fmt.Println(k, v)}マップのゼロ値は nilです。
nil マップへの書き込みはパニックになりますので、必ず make か初期化してから使います。
構造体とメソッド
Section titled “構造体とメソッド”構造体は、関連する複数の値をフィールドとしてひとまとまりにした独自の型です。 フィールド名が大文字で始まると他パッケージから参照でき、小文字で始まるとパッケージ内だけで使えます。
type Person struct { Name string // 先頭が大文字 = 公開フィールド Age int addr string // 先頭が小文字 = 非公開フィールド}
// 初期化p1 := Person{Name: "Alice", Age: 30} // 推奨p2 := Person{"Bob", 25, "Tokyo"} // フィールド順(非推奨)p3 := new(Person) // *Person、ゼロ値
// 埋め込み(継承ではなくコンポジション)type Employee struct { Person // フィールドとメソッドを昇格 Department string}
e := Employee{Person: Person{Name: "Carol", Age: 28}, Department: "Eng"}fmt.Println(e.Name) // Person.Name に透過アクセス
// Go 1.27から:昇格したフィールドをキーに直接指定できるe2 := Employee{Name: "Eve", Age: 32, Department: "Sales"}Go 1.27 から、構造体リテラルのキーには、その構造体で有効なフィールドセレクタを指定できます。上の Name と Age は、埋め込まれた Person から昇格したフィールドです。
メソッドは特定の型に関連付ける関数です。 関数名の前にレシーバを指定し、値レシーバなら値のコピーを、ポインタレシーバなら元の値へのポインタを受け取ります。
// 値レシーバ(コピーを受け取る)func (p Person) Greet() string { return "Hi, I'm " + p.Name}
// ポインタレシーバ(元の値を変更できる)func (p *Person) Birthday() { p.Age++}
p := Person{Name: "Dave", Age: 20}p.Birthday() // Go が自動的に &p を渡すfmt.Println(p.Age) // 21ポインタレシーバを選ぶ基準
- 構造体を変更する必要がある場合 → ポインタレシーバ
- 構造体が大きい場合(コピーコスト)→ ポインタレシーバ
- それ以外 → 値レシーバでも可
一つのメソッドでポインタレシーバを使ったら、同じ型の他メソッドも統一するのがベストプラクティスです。
構造体タグはフィールドに付けられるメタデータです。
タグ自体に言語機能はなく、encoding/json や ORM などがリフレクションを通じて解釈します。
慣例として、空白区切りで json:"id" のような キー:"値" を記述します。
type User struct { ID int `json:"id" db:"user_id"` Email string `json:"email,omitempty"`}
// encoding/json が reflect でタグを参照data, _ := json.Marshal(User{ID: 1, Email: ""})// → {"id":1} (omitempty で Email は省略)インターフェース
Section titled “インターフェース”インターフェースの仕組み
Section titled “インターフェースの仕組み”インターフェースは「その型が持つべきメソッドの集合」を定義する型です。 必要なメソッドをすべて実装した型は、宣言なしでそのインターフェースを実装します。 具体的な型ではなく振る舞いに依存した設計に使います。
type Shape interface { Area() float64 Perimeter() float64}
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 }
// 実装は暗黙的(implements キーワード不要)var s Shape = Circle{Radius: 5}fmt.Println(s.Area())型アサーション・型スイッチ
Section titled “型アサーション・型スイッチ”// 型アサーションc, ok := s.(Circle)if ok { fmt.Println("radius:", c.Radius)}
// ok なしは失敗時にパニックc2 := s.(Circle)
// 型スイッチ(前述の switch セクション参照)空インターフェース・any
Section titled “空インターフェース・any”// any は interface{} のエイリアス(Go 1.18〜)func printAny(v any) { fmt.Printf("%T: %v\n", v, v)}
printAny(42)printAny("hello")printAny([]int{1, 2, 3})よく使う小さなインターフェース
Section titled “よく使う小さなインターフェース”// io.Reader / io.Writertype Reader interface { Read(p []byte) (n int, err error) }type Writer interface { Write(p []byte) (n int, err error) }
// fmt.Stringer:String() を実装すると fmt.Println で出力可能type Stringer interface { String() string }
func (p Person) String() string { return fmt.Sprintf("%s (%d)", p.Name, p.Age)}
fmt.Println(Person{Name: "Eve", Age: 22}) // Eve (22)エラーハンドリング
Section titled “エラーハンドリング”基本パターン
Section titled “基本パターン”// error は組み込みインターフェース// type error interface { Error() string }
f, err := os.Open("file.txt")if err != nil { return fmt.Errorf("open file: %w", err) // %w でラップ}defer f.Close()カスタムエラー
Section titled “カスタムエラー”type NotFoundError struct { Resource string ID int}
func (e *NotFoundError) Error() string { return fmt.Sprintf("%s (id=%d) not found", e.Resource, e.ID)}
func findUser(id int) (*User, error) { if id <= 0 { return nil, &NotFoundError{Resource: "user", ID: id} } // ...}errors パッケージ
Section titled “errors パッケージ”import "errors"
// errors.Is:sentinel エラーを %w でラップして伝播させ、チェーンを辿って比較var ErrNotFound = errors.New("not found")
func fetchUser(id int) (*User, error) { if id <= 0 { return nil, fmt.Errorf("fetchUser: %w", ErrNotFound) // %w でラップ } // ... return nil, nil}
_, err := fetchUser(-1) // fetchUser は (*User, error) を返すので _ で捨てるif errors.Is(err, ErrNotFound) { fmt.Println("not found")}
// errors.As:カスタムエラー型をチェーンから取り出して型変換func findItem(id int) error { return fmt.Errorf("findItem: %w", &NotFoundError{Resource: "item", ID: id})}
var nfe *NotFoundErrorif errors.As(findItem(42), &nfe) { fmt.Println("resource:", nfe.Resource) // "item"}
// Go 1.26〜:errors.As の型安全なジェネリクス版if nfe, ok := errors.AsType[*NotFoundError](findItem(42)); ok { fmt.Println("resource:", nfe.Resource) // "item"}
// errors.Join(Go 1.20〜):複数エラーを結合err1 := errors.New("err1")err2 := errors.New("err2")combined := errors.Join(err1, err2)panic と recover
Section titled “panic と recover”// panic:回復不能なエラーに使う(通常は error を返す)func mustPositive(n int) int { if n <= 0 { panic(fmt.Sprintf("must be positive, got %d", n)) } return n}
// recover:defer 内でのみ有効func safeDiv(a, b int) (result int, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("recovered: %v", r) } }() return a / b, nil}goroutine・channel
Section titled “goroutine・channel”並行処理モデル概念図
Section titled “並行処理モデル概念図”goroutine の実行モデル(GMP モデル)
G = Goroutine(軽量スレッド、初期スタック 2KB・必要に応じて拡張)M = OS ThreadP = Processor(GOMAXPROCS で制御)
P1 P2 ┌────────────┐ ┌────────────┐ │ G1 running │ │ G3 running │ │ ┌────────┐ │ │ ┌────────┐ │ │ │ G2 │ │ │ │ G4 │ │ ← ローカルキュー │ │ G5 │ │ │ └────────┘ │ │ └────────┘ │ └──────┬─────┘ └──────┬─────┘ │ │ │ M1 M2 ← OS スレッドgoroutine
Section titled “goroutine”// go キーワードで非同期実行go func() { fmt.Println("goroutine")}()
// 完了を待つvar wg sync.WaitGroupfor i := 0; i < 5; i++ { wg.Add(1) go func(n int) { defer wg.Done() fmt.Println(n) }(i) // i を引数で渡す(クロージャキャプチャを避ける)}wg.Wait()channel
Section titled “channel”// 生成ch := make(chan int) // 非バッファチャネルbch := make(chan int, 10) // バッファチャネル(容量10)
// 送受信(非バッファは送受信を別ゴルーチンで行う)go func() { ch <- 42 }() // 別ゴルーチンで送信v := <-ch // メインゴルーチンで受信
// バッファチャネルは容量内なら同一ゴルーチンでも送受信可bch <- 1bch <- 2fmt.Println(<-bch, <-bch)
// クローズclose(ch)v, ok := <-ch // ok=false ならクローズ済み・ゼロ値が返る
// range で受信(クローズまで)for v := range ch { fmt.Println(v)}チャネルの方向性
Section titled “チャネルの方向性”// 送信専用・受信専用チャネルで意図を明確化func producer(ch chan<- int) { // 送信専用 for i := 0; i < 5; i++ { ch <- i } close(ch)}
func consumer(ch <-chan int) { // 受信専用 for v := range ch { fmt.Println(v) }}
ch := make(chan int)go producer(ch)consumer(ch)select
Section titled “select”// 複数チャネルを同時に待機select {case v := <-ch1: fmt.Println("ch1:", v)case v := <-ch2: fmt.Println("ch2:", v)case ch3 <- data: fmt.Println("sent to ch3")case <-time.After(1 * time.Second): fmt.Println("timeout")default: fmt.Println("no channel ready") // ノンブロッキング}sync パッケージ
Section titled “sync パッケージ”// Mutex:共有リソース保護var mu sync.Mutexvar count int
mu.Lock()count++mu.Unlock()
// RWMutex:読み取り多・書き込み少の場合に有効var rw sync.RWMutexrw.RLock() // 複数ゴルーチンが同時に読める// ...rw.RUnlock()
// Once:初期化を一度だけ実行var once sync.Onceonce.Do(func() { /* 初期化処理 */ })
// atomic:シンプルなカウンタにvar n atomic.Int64n.Add(1)fmt.Println(n.Load())コンテキスト
Section titled “コンテキスト”コンテキストツリー
context.Background() └── WithCancel → ctx, cancel └── WithTimeout(ctx, 5s) → ctx2, cancel2 └── WithValue(ctx2, key, val) → ctx3 ↓ キャンセル・タイムアウトは 子コンテキストに伝播するimport "context"
// タイムアウト付きコンテキストctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel() // リソースリークを防ぐため必ず呼ぶ
// HTTP リクエスト例req, err := http.NewRequestWithContext(ctx, "GET", "https://example.com", nil)if err != nil { return err}
resp, err := http.DefaultClient.Do(req)if err != nil { if errors.Is(err, context.DeadlineExceeded) { fmt.Println("timeout") } return err}defer resp.Body.Close()
// キャンセル可能コンテキストctx2, cancel2 := context.WithCancel(context.Background())go func() { <-ctx2.Done() // キャンセルを検知 fmt.Println("cancelled:", ctx2.Err())}()cancel2()
// 値の伝達(リクエストスコープの少量メタデータに限定する)// ビジネスロジックの引数渡しには使わないtype ctxKey stringconst userKey ctxKey = "user"
ctx3 := context.WithValue(context.Background(), userKey, "alice")user, ok := ctx3.Value(userKey).(string) // 型アサーションは ok チェック付きジェネリクス
Section titled “ジェネリクス”Go 1.18 で導入。型パラメータで再利用可能なコードを書けます。
型パラメータの基本
Section titled “型パラメータの基本”// T はなんでも受け取れる(any = interface{})func Map[T, U any](s []T, f func(T) U) []U { result := make([]U, len(s)) for i, v := range s { result[i] = f(v) } return result}
doubled := Map([]int{1, 2, 3}, func(n int) int { return n * 2 })// [2 4 6]型制約(Constraint)
Section titled “型制約(Constraint)”import "cmp"
// cmp.Ordered:<, >, == が使える型(int, float64, string など)func Min[T cmp.Ordered](a, b T) T { if a < b { return a } return b}
Min(3, 5) // 3 (int)Min(3.14, 2.71) // 2.71 (float64)Min("b", "a") // "a" (string)
// カスタム制約type Number interface { ~int | ~int64 | ~float64}
func Sum[T Number](nums []T) T { var total T for _, n := range nums { total += n } return total}ジェネリック構造体
Section titled “ジェネリック構造体”type Stack[T any] struct { items []T}
func (s *Stack[T]) Push(v T) { s.items = append(s.items, v)}
func (s *Stack[T]) Pop() (T, bool) { if len(s.items) == 0 { var zero T return zero, false } v := s.items[len(s.items)-1] s.items = s.items[:len(s.items)-1] return v, true}
s := Stack[string]{}s.Push("Go")s.Push("Generics")v, _ := s.Pop() // "Generics"ジェネリックメソッド(Go 1.27〜)
Section titled “ジェネリックメソッド(Go 1.27〜)”Go 1.27 から、メソッド自身が型パラメータを宣言できます。型に属する操作として、複数の型に対応するメソッドを定義できます。
type Converter struct{}
func (Converter) Convert[T any](v T) T { return v}
c := Converter{}n := c.Convert(42) // T = ints := c.Convert("hello") // T = stringインターフェースのメソッドは型パラメータを宣言できず、ジェネリックメソッドでインターフェースメソッドを実装することもできません。
ジェネリック関数の型推論(Go 1.27〜)
Section titled “ジェネリック関数の型推論(Go 1.27〜)”ジェネリック関数を関数型へ代入・変換する場合や、複合リテラル、チャネル送信で使う場合も、代入先の型から型引数を推論できます。
func Stringify[T any](v T) string { return fmt.Sprint(v)}
type IntFormatter func(int) string
var format IntFormatter = Stringifyformatters := []IntFormatter{Stringify}
ch := make(chan IntFormatter, 1)ch <- StringifyGo 1.23 以降では、iter.Seq を返す関数を range で直接イテレートできます。
呼び出し側が途中で break した場合は、yield が false を返すため生成側も停止できます。
import "iter"
// n-1 から 0 までを返すイテレータfunc Backward(n int) iter.Seq[int] { return func(yield func(int) bool) { for i := n - 1; i >= 0; i-- { if !yield(i) { // 呼び出し側が break した return } } }}
for i := range Backward(3) { fmt.Println(i) // 2, 1, 0}
// maps.Keys などの iter.Seq は slices.Collect でスライス化できるkeys := slices.Collect(maps.Keys(m))// xxx_test.go ファイルに書くpackage mypackage_test
import ( "os" "path/filepath" "testing"
"github.com/yourname/yourproject/mypackage")
func TestAdd(t *testing.T) { got := mypackage.Add(2, 3) if got != 5 { t.Errorf("Add(2,3) = %d; want 5", got) }}
// テーブル駆動テスト(推奨パターン)func TestDivide(t *testing.T) { tests := []struct { name string a, b float64 want float64 wantErr bool }{ {"normal", 10, 2, 5, false}, {"division by zero", 10, 0, 0, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := mypackage.Divide(tt.a, tt.b) if (err != nil) != tt.wantErr { t.Errorf("unexpected error: %v", err) } if got != tt.want { t.Errorf("got %v, want %v", got, tt.want) } }) }}
// ベンチマークfunc BenchmarkAdd(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { mypackage.Add(2, 3) }}// ヘルパー内の失敗を呼び出し元の行に表示するfunc requireNoError(t *testing.T, err error) { t.Helper() if err != nil { t.Fatal(err) }}
func TestWriteFile(t *testing.T) { t.Setenv("APP_ENV", "test") // テスト終了時に自動で元へ戻る dir := t.TempDir() // テスト終了時に自動削除 t.Cleanup(func() { // モックのリセットなど、任意の後始末を書く })
path := filepath.Join(dir, "output.txt") requireNoError(t, os.WriteFile(path, []byte("hello"), 0o644))}go test -cover ./... # カバレッジを表示標準ライブラリ早引き
Section titled “標準ライブラリ早引き”// 出力fmt.Print("no newline")fmt.Println("with newline")fmt.Printf("formatted %s %d %v\n", "str", 42, true)fmt.Fprintf(os.Stderr, "error: %v\n", err)
// 文字列生成s := fmt.Sprintf("Hello, %s!", name)msg := fmt.Errorf("wrap: %w", err)
// 主要フォーマット動詞// %v デフォルト表現 %+v フィールド名付き %#v Go構文表現// %T 型名 %d 整数 %f 浮動小数点// %s 文字列 %q クォート文字列 %x 16進数// %p ポインタアドレス %b 2進数 %e 指数表記strings
Section titled “strings”import "strings"
strings.Contains("Hello", "ell") // truestrings.HasPrefix("Hello", "He") // truestrings.HasSuffix("Hello", "lo") // truestrings.Count("cheese", "e") // 3strings.Index("Hello", "ll") // 2strings.Replace("oink oink", "oink", "moo", 1) // "moo oink"strings.ReplaceAll("oink oink", "oink", "moo") // "moo moo"strings.ToUpper("hello") // "HELLO"strings.ToLower("HELLO") // "hello"strings.TrimSpace(" hello ") // "hello"strings.Trim("##hello##", "#") // "hello"strings.Split("a,b,c", ",") // ["a" "b" "c"]strings.Join([]string{"a","b"}, "-") // "a-b"strings.Fields("a b\tc") // ["a" "b" "c"]
// Builder(ループ内での大量結合に有効。単純な + は少数なら問題ない)var sb strings.Builderfor i := 0; i < 5; i++ { fmt.Fprintf(&sb, "%d", i)}sb.String() // "01234"strconv
Section titled “strconv”import "strconv"
strconv.Itoa(42) // "42"strconv.Atoi("42") // 42, nilstrconv.ParseFloat("3.14", 64) // 3.14, nilstrconv.FormatFloat(3.14, 'f', 2, 64)// "3.14"strconv.ParseBool("true") // true, nilstrconv.FormatBool(true) // "true"import "sort"
nums := []int{5, 2, 4, 1, 3}sort.Ints(nums) // [1 2 3 4 5]
words := []string{"b", "a", "c"}sort.Strings(words)
// カスタムソートsort.Slice(nums, func(i, j int) bool { return nums[i] > nums[j] // 降順})// slices パッケージ(Go 1.21〜、ジェネリクス版)import ( "cmp" "slices")slices.Sort(nums) // 昇順idx, found := slices.BinarySearch(nums, 3) // 昇順ソート済みに対して使う
// 降順ソート:cmp.Compare を使うと安全slices.SortFunc(nums, func(a, b int) int { return cmp.Compare(b, a) })maps(Go 1.21〜、イテレータ API は Go 1.23〜)
Section titled “maps(Go 1.21〜、イテレータ API は Go 1.23〜)”import ( "maps" "slices")
m := map[string]int{"a": 1, "b": 2}m2 := maps.Clone(m) // コピーmaps.Copy(m2, m) // m の全エントリを m2 にマージdelete(m, "a") // キー削除は組み込み関数keys := slices.Collect(maps.Keys(m)) // キー一覧をスライスにimport "time"
now := time.Now()t := time.Date(2026, 6, 1, 12, 0, 0, 0, time.Local)
// フォーマット(Go独自:2006-01-02 15:04:05 が基準時刻)now.Format("2006-01-02 15:04:05")now.Format(time.RFC3339) // "2026-06-22T12:00:00+09:00"t, _ = time.Parse("2006-01-02", "2026-06-22")
// 計算later := now.Add(24 * time.Hour)diff := later.Sub(now) // time.Durationfmt.Println(diff.Hours()) // 24.0
// タイマーtime.Sleep(100 * time.Millisecond)timer := time.NewTimer(1 * time.Second)ticker := time.NewTicker(500 * time.Millisecond)defer ticker.Stop()os / filepath
Section titled “os / filepath”import ( "os" "path/filepath")
// ファイル読み書きdata, err := os.ReadFile("file.txt")err = os.WriteFile("out.txt", data, 0644)
// ディレクトリ操作entries, _ := os.ReadDir(".")for _, e := range entries { fmt.Println(e.Name(), e.IsDir())}os.MkdirAll("a/b/c", 0755)os.Remove("file.txt")os.RemoveAll("dir/")
// 環境変数home := os.Getenv("HOME")os.Setenv("KEY", "value")
// パス操作filepath.Join("a", "b", "c") // OS 依存の区切り文字で結合filepath.Base("/a/b/file.txt") // "file.txt"filepath.Dir("/a/b/file.txt") // "/a/b"filepath.Ext("file.txt") // ".txt"filepath.Abs("relative/path") // 絶対パスencoding/json
Section titled “encoding/json”import "encoding/json"
type Point struct { X int `json:"x"` Y int `json:"y"`}
// エンコードp := Point{X: 1, Y: 2}b, err := json.Marshal(p) // []bytes, err := json.MarshalIndent(p, "", " ") // 整形
// デコードvar p2 Pointerr = json.Unmarshal(b, &p2)
// ストリームenc := json.NewEncoder(os.Stdout)enc.Encode(p)
dec := json.NewDecoder(os.Stdin) // r は io.Reader なら何でも可dec.Decode(&p2)
// 型不明な場合var v anyjson.Unmarshal(data, &v)if m, ok := v.(map[string]any); ok { fmt.Println(m["key"])}encoding/json/v2(Go 1.27〜)
Section titled “encoding/json/v2(Go 1.27〜)”encoding/json/v2 は、オプションで動作を調整できる新しいJSON APIです。不正なUTF-8やJSONオブジェクト内の重複した名前を拒否するなど、従来の encoding/json より厳格なデフォルトを持ちます。
import jsonv2 "encoding/json/v2"
type Config struct { Port int `json:"port"`}
data, err := jsonv2.Marshal(Config{Port: 8080})
var cfg Configerr = jsonv2.Unmarshal( data, &cfg, jsonv2.RejectUnknownMembers(true),)従来の encoding/json も引き続きサポートされるため、v2への移行は必須ではありません。既存APIの動作は維持されますが、内部実装の変更によりエラーメッセージの文面が変わる場合があります。
uuid(Go 1.27〜)
Section titled “uuid(Go 1.27〜)”import "uuid"
id := uuid.New() // 用途に特定のバージョン要件がなければこちらを使用id7 := uuid.NewV7() // 時系列で並べやすいUUID v7
parsed, err := uuid.Parse("f81d4fae-7dec-11d0-a765-00a0c91e6bf6")if err != nil { log.Fatal(err)}
fmt.Println(id, id7, parsed)uuid.MustParse は不正な文字列でpanicするため、固定値やテストでの利用に向いています。
net/http
Section titled “net/http”import ( "bytes" "context" "errors" "io" "log" "net/http" "os" "os/signal" "syscall" "time")
// 運用環境ではタイムアウト設定を推奨(デフォルトクライアントは timeout なし)client := &http.Client{Timeout: 10 * time.Second}
// GETresp, err := client.Get("https://example.com")if err != nil { return err }defer resp.Body.Close()body, _ := io.ReadAll(resp.Body)
// カスタムリクエスト(POST など)req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(jsonData))if err != nil { return err }req.Header.Set("Content-Type", "application/json")resp2, err := client.Do(req)if err != nil { return err }defer resp2.Body.Close()
// HTTPサーバーmux := http.NewServeMux()mux.HandleFunc("GET /hello/{name}", func(w http.ResponseWriter, r *http.Request) { name := r.PathValue("name") // Go 1.22〜 パターンマッチ fmt.Fprintf(w, "Hello, %s!", name)})
// 運用環境ではタイムアウトと graceful shutdown の設定を推奨// タイムアウト値は、アップロードやストリーミングなどの用途に合わせて調整するserver := &http.Server{ Addr: ":8080", Handler: mux, ReadHeaderTimeout: 5 * time.Second, ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 60 * time.Second,}go func() { if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Printf("server error: %v", err) }}()
shutdownSignal, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)defer stop()<-shutdownSignal.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)defer cancel()if err := server.Shutdown(shutdownCtx); err != nil { log.Printf("shutdown error: %v", err)}log/slog(Go 1.21〜 構造化ログ)
Section titled “log/slog(Go 1.21〜 構造化ログ)”import "log/slog"
// デフォルトロガーslog.Info("server started", "port", 8080)slog.Error("failed", "err", err)
// JSON ロガーlogger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelDebug,}))logger.Info("user logged in", "userID", 42, "ip", "127.0.0.1")// {"time":"...","level":"INFO","msg":"user logged in","userID":42,"ip":"127.0.0.1"}
// グループlogger.With("requestID", "abc123").Info("processing")Tips & よくあるミス
Section titled “Tips & よくあるミス”goroutineリーク
Section titled “goroutineリーク”// NG: goroutine が永遠に待ち続けるgo func() { ch <- result // 受信者がいないとリーク}()
// OK: コンテキストでキャンセルを伝えるgo func() { select { case ch <- result: case <-ctx.Done(): return }}()nil インターフェース
Section titled “nil インターフェース”// NG: *MyError が nil でも、interface は nil にならないfunc getError() error { var err *MyError = nil return err // error インターフェースは non-nil!}
if getError() != nil { // true になってしまう fmt.Println("this prints unexpectedly")}
// OKfunc getError() error { return nil // 直接 nil を返す}スライスの容量事前確保
Section titled “スライスの容量事前確保”// NG: append のたびにコピーが発生する可能性result := []int{}for i := 0; i < 10000; i++ { result = append(result, i)}
// OK: 事前に確保result := make([]int, 0, 10000)for i := 0; i < 10000; i++ { result = append(result, i)}for ループ変数のクロージャキャプチャ(Go 1.22 で修正済み)
Section titled “for ループ変数のクロージャキャプチャ(Go 1.22 で修正済み)”// Go 1.21 以前: 全ゴルーチンが同じ i を参照してしまうfor i := 0; i < 5; i++ { go func() { fmt.Println(i) }() // 全部 5 になる可能性}
// Go 1.22 以降: ループ変数がイテレーションごとに新しい変数に// 上記コードは正しく 0,1,2,3,4 を出力する
// Go 1.21 以前の対処for i := 0; i < 5; i++ { i := i // シャドーイング go func() { fmt.Println(i) }()}モジュール・パッケージ管理
Section titled “モジュール・パッケージ管理”# モジュール初期化go mod init github.com/yourname/yourproject
# Go 1.27go get go@1.27
# 依存追加go get github.com/some/package@v1.2.3
# 依存整理(不要なものを削除)go mod tidy
# ベンダリングgo mod vendor
# 主要コマンドgo build ./... # ビルドgo test ./... # テストgo test -race ./... # レースコンディション検出go vet ./... # 静的解析go fmt ./... # フォーマット公式リファレンス:pkg.go.dev / go.dev/ref/spec