コンテンツにスキップ

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() がエントリポイント

// var
var name = "Gopher" // string(初期値から型を推論)
var age int // 初期値なしでは型の指定が必要。ゼロ値 = 0
var 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 〜 127
var i64 int64 = 9223372036854775807
var u uint = 42 // 符号なし
var ptr uintptr // ポインタ幅
// 浮動小数点
var f32 float32 = 3.14
var 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 := 42
f := float64(i) // 必ず明示して変換する
s := fmt.Sprintf("%d", i) // 数値→文字列
n, _ := strconv.Atoi("123") // 文字列→数値
const Pi = 3.14159
// iota:連番定数
type Weekday int
const (
Sunday Weekday = iota // 0
Monday // 1
Tuesday // 2
)
// ビットフラグパターン
type Permission uint
const (
Read Permission = 1 << iota // 1
Write // 2
Execute // 4
)
x := 10
p := &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-else
if 0 < x {
fmt.Println("positive")
} else if x < 0 {
fmt.Println("negative")
} else {
fmt.Println("zero")
}
// 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(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...) // スライスを展開

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() // 1
c() // 2

init はパッケージの初期化時に自動で呼ばれる特別な関数です。
main 関数より前に実行され、1つのパッケージ内に複数定義できます。

通常のアプリケーション初期化は main や明示的な初期化関数で行う方が、依存関係とエラー処理を追いやすくなります。
init はパッケージ内部の登録処理など、実行順序を意識せずに済む用途に限定するのが無難です。

// パッケージ初期化時に自動実行(複数定義可)
func init() {
// ドライバやハンドラの登録など
}

配列(固定長・値型)
┌───┬───┬───┬───┬───┐
│ 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 := b
d[0] = 99 // b は変わらない

スライスは配列の一部または全体を参照する、可変長のビューです。

配列との主な違いは、長さが型の一部ではないこと、代入しても要素全体はコピーされず基底配列を共有し得ること、append で長さを増やせます。

// 生成
s := []int{1, 2, 3}
s2 := make([]int, 5) // len=5, cap=5
s3 := 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 := 2
s = append(s[:i], s[i+1:]...)
// 長さ・容量
fmt.Println(len(s), cap(s))

sub := s[1:3] のようにスライシングすると、subs は同じ配列を指します。 sub を変更すると s にも影響します。独立させるには copy を使いましょう。

マップはキーと値を対応付けて保持するコレクションです。 要素の順序は保証されず、キーには比較可能な型(数値、文字列、ポインタ、配列、比較可能な構造体など)を使えます。 slice、map、func はキーには使えません。

// 生成
m := map[string]int{"a": 1, "b": 2}
m2 := make(map[string]int)
// 操作
m["c"] = 3
delete(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 か初期化してから使います。


構造体は、関連する複数の値をフィールドとしてひとまとまりにした独自の型です。 フィールド名が大文字で始まると他パッケージから参照でき、小文字で始まるとパッケージ内だけで使えます。

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 から、構造体リテラルのキーには、その構造体で有効なフィールドセレクタを指定できます。上の NameAge は、埋め込まれた 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 は省略)

インターフェースは「その型が持つべきメソッドの集合」を定義する型です。 必要なメソッドをすべて実装した型は、宣言なしでそのインターフェースを実装します。 具体的な型ではなく振る舞いに依存した設計に使います。

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())
// 型アサーション
c, ok := s.(Circle)
if ok {
fmt.Println("radius:", c.Radius)
}
// ok なしは失敗時にパニック
c2 := s.(Circle)
// 型スイッチ(前述の switch セクション参照)
// 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.Writer
type 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)

// 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()
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}
}
// ...
}
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 *NotFoundError
if 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:回復不能なエラーに使う(通常は 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 の実行モデル(GMP モデル)
G = Goroutine(軽量スレッド、初期スタック 2KB・必要に応じて拡張)
M = OS Thread
P = Processor(GOMAXPROCS で制御)
P1 P2
┌────────────┐ ┌────────────┐
│ G1 running │ │ G3 running │
│ ┌────────┐ │ │ ┌────────┐ │
│ │ G2 │ │ │ │ G4 │ │ ← ローカルキュー
│ │ G5 │ │ │ └────────┘ │
│ └────────┘ │ └──────┬─────┘
└──────┬─────┘ │
│ │
M1 M2 ← OS スレッド
// go キーワードで非同期実行
go func() {
fmt.Println("goroutine")
}()
// 完了を待つ
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
fmt.Println(n)
}(i) // i を引数で渡す(クロージャキャプチャを避ける)
}
wg.Wait()
// 生成
ch := make(chan int) // 非バッファチャネル
bch := make(chan int, 10) // バッファチャネル(容量10)
// 送受信(非バッファは送受信を別ゴルーチンで行う)
go func() { ch <- 42 }() // 別ゴルーチンで送信
v := <-ch // メインゴルーチンで受信
// バッファチャネルは容量内なら同一ゴルーチンでも送受信可
bch <- 1
bch <- 2
fmt.Println(<-bch, <-bch)
// クローズ
close(ch)
v, ok := <-ch // ok=false ならクローズ済み・ゼロ値が返る
// range で受信(クローズまで)
for v := range ch {
fmt.Println(v)
}
// 送信専用・受信専用チャネルで意図を明確化
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 {
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") // ノンブロッキング
}
// Mutex:共有リソース保護
var mu sync.Mutex
var count int
mu.Lock()
count++
mu.Unlock()
// RWMutex:読み取り多・書き込み少の場合に有効
var rw sync.RWMutex
rw.RLock() // 複数ゴルーチンが同時に読める
// ...
rw.RUnlock()
// Once:初期化を一度だけ実行
var once sync.Once
once.Do(func() { /* 初期化処理 */ })
// atomic:シンプルなカウンタに
var n atomic.Int64
n.Add(1)
fmt.Println(n.Load())

コンテキストツリー
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 string
const userKey ctxKey = "user"
ctx3 := context.WithValue(context.Background(), userKey, "alice")
user, ok := ctx3.Value(userKey).(string) // 型アサーションは ok チェック付き

Go 1.18 で導入。型パラメータで再利用可能なコードを書けます。

// 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]
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
}
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 = int
s := 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 = Stringify
formatters := []IntFormatter{Stringify}
ch := make(chan IntFormatter, 1)
ch <- Stringify

Go 1.23 以降では、iter.Seq を返す関数を range で直接イテレートできます。 呼び出し側が途中で break した場合は、yieldfalse を返すため生成側も停止できます。

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))
}
Terminal window
go test -cover ./... # カバレッジを表示

// 出力
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 指数表記
import "strings"
strings.Contains("Hello", "ell") // true
strings.HasPrefix("Hello", "He") // true
strings.HasSuffix("Hello", "lo") // true
strings.Count("cheese", "e") // 3
strings.Index("Hello", "ll") // 2
strings.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.Builder
for i := 0; i < 5; i++ {
fmt.Fprintf(&sb, "%d", i)
}
sb.String() // "01234"
import "strconv"
strconv.Itoa(42) // "42"
strconv.Atoi("42") // 42, nil
strconv.ParseFloat("3.14", 64) // 3.14, nil
strconv.FormatFloat(3.14, 'f', 2, 64)// "3.14"
strconv.ParseBool("true") // true, nil
strconv.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.Duration
fmt.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()
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") // 絶対パス
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) // []byte
s, err := json.MarshalIndent(p, "", " ") // 整形
// デコード
var p2 Point
err = 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 any
json.Unmarshal(data, &v)
if m, ok := v.(map[string]any); ok {
fmt.Println(m["key"])
}

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 Config
err = jsonv2.Unmarshal(
data,
&cfg,
jsonv2.RejectUnknownMembers(true),
)

従来の encoding/json も引き続きサポートされるため、v2への移行は必須ではありません。既存APIの動作は維持されますが、内部実装の変更によりエラーメッセージの文面が変わる場合があります。

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するため、固定値やテストでの利用に向いています。

import (
"bytes"
"context"
"errors"
"io"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
// 運用環境ではタイムアウト設定を推奨(デフォルトクライアントは timeout なし)
client := &http.Client{Timeout: 10 * time.Second}
// GET
resp, 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)
}
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")

// NG: goroutine が永遠に待ち続ける
go func() {
ch <- result // 受信者がいないとリーク
}()
// OK: コンテキストでキャンセルを伝える
go func() {
select {
case ch <- result:
case <-ctx.Done():
return
}
}()
// 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")
}
// OK
func getError() error {
return nil // 直接 nil を返す
}
// 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) }()
}

Terminal window
# モジュール初期化
go mod init github.com/yourname/yourproject
# Go 1.27
go 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