Go言語 チートシート

  • 変数宣言:
    // 型を指定して宣言 (ゼロ値で初期化)
    var count int
    // 初期値を指定して宣言 (型推論)
    var message = "Hello"
    // 関数内での短縮宣言 (型推論)
    value := 100
  • 複数変数宣言:
    // まとめて宣言
    var x, y int
    var a, b = 1, "text"
    c, d := true, 3.14
    // グループ化して宣言
    var ( appVersion string = "1.0.0" debugMode bool = false
    )
  • 定数宣言:
    // 型なし定数 (使用時に型が決定)
    const Pi = 3.14159
    // 型付き定数
    const MaxSize int = 1024
    // 連続する定数 (iota)
    const ( Sunday = iota // 0 Monday // 1 Tuesday // 2
    )
    // iotaの応用
    const ( _ = 1 << (10 * iota) // 読み飛ばし KiB // 1 << (10*1) = 1024 MiB // 1 << (10*2) = 1048576 GiB // 1 << (10*3) = 1073741824
    )
  • if / else if / else:
    score := 75
    if score >= 80 { fmt.Println("Great")
    } else if score >= 60 { fmt.Println("Good")
    } else { fmt.Println("Needs Improvement")
    }
    // 条件判定前に簡単なステートメントを実行
    if err := process(); err != nil { fmt.Println("Error:", err)
    }
  • switch:
    // 基本的なswitch
    day := "Monday"
    switch day {
    case "Sunday", "Saturday": fmt.Println("Weekend! ")
    case "Monday": fmt.Println("Start of the week...")
    default: fmt.Println("Weekday")
    }
    // 条件式を持つswitch (if/else ifの代替)
    num := 15
    switch {
    case num < 10: fmt.Println("Single digit")
    case num < 100: fmt.Println("Double digit")
    default: fmt.Println("Large number")
    }
    // フォールスルー (fallthrough)
    value := 1
    switch value {
    case 1: fmt.Println("Value is 1") fallthrough // 次のcaseも実行
    case 2: fmt.Println("Value is 1 or 2")
    default: fmt.Println("Other value")
    }
  • for:
    // C言語風forループ
    for i := 0; i < 5; i++ { fmt.Println(i)
    }
    // 条件式のみ (while風)
    n := 0
    for n < 5 { fmt.Println(n) n++
    }
    // 無限ループ
    // for {
    // fmt.Println("Looping forever...")
    // time.Sleep(1 * time.Second)
    // }
    // スライスやマップの反復 (range)
    nums := []int{10, 20, 30}
    for index, value := range nums { fmt.Printf("Index: %d, Value: %d\n", index, value)
    }
    // インデックスのみ
    for index := range nums { fmt.Println("Index:", index)
    }
    // 値のみ
    for _, value := range nums { fmt.Println("Value:", value)
    }
    // マップの反復
    colors := map[string]string{"red": "#ff0000", "green": "#00ff00"}
    for key, value := range colors { fmt.Printf("Key: %s, Value: %s\n", key, value)
    }
    // 文字列の反復 (rune単位)
    for index, runeValue := range "Go言語" { fmt.Printf("%#U starts at byte position %d\n", runeValue, index)
    }
  • defer: 関数終了時に実行される処理を登録。リソース解放などに便利。
    func readFile(filename string) error { file, err := os.Open(filename) if err != nil { return err } defer file.Close() // 関数終了時に必ず実行される // ファイル処理... return nil
    }
  • 基本的な関数定義:
    func add(x int, y int) int { return x + y
    }
    // 引数の型が同じ場合は省略可能
    func multiply(x, y int) int { return x * y
    }
  • 複数の戻り値:
    func divide(numerator, denominator int) (int, error) { if denominator == 0 { return 0, fmt.Errorf("cannot divide by zero") } return numerator / denominator, nil
    }
    result, err := divide(10, 2)
    if err != nil { fmt.Println("Error:", err)
    } else { fmt.Println("Result:", result)
    }
  • 名前付き戻り値:
    // 戻り値の変数を事前に宣言
    func calculate(a, b int) (sum int, difference int) { sum = a + b difference = a - b // return文で変数を指定しなくても良い (naked return) return
    }
    s, d := calculate(5, 3) // s = 8, d = 2
  • 可変長引数:
    func sumNumbers(nums ...int) int { total := 0 for _, num := range nums { total += num } return total
    }
    sum := sumNumbers(1, 2, 3, 4, 5) // sum = 15
    // スライスを渡すことも可能
    numbers := []int{10, 20}
    sum = sumNumbers(numbers...) // sum = 30
  • 無名関数 (クロージャ):
    // 変数に関数を代入
    greet := func(name string) { fmt.Println("Hello,", name)
    }
    greet("Alice")
    // クロージャ (関数が定義されたスコープの変数を参照)
    func incrementer() func() int { count := 0 return func() int { count++ return count }
    }
    inc := incrementer()
    fmt.Println(inc()) // 1
    fmt.Println(inc()) // 2
  • 配列 (Array): 固定長
    var arr [3]int // [0 0 0]
    arr[0] = 1
    fmt.Println(arr, len(arr)) // [1 0 0] 3
    arr2 := [...]string{"apple", "banana"} // 要素数から長さを推論
    fmt.Println(arr2, len(arr2)) // [apple banana] 2
  • スライス (Slice): 可変長、配列への参照
    // スライスリテラルで作成
    s1 := []int{1, 2, 3}
    fmt.Println(s1, len(s1), cap(s1)) // [1 2 3] 3 3
    // makeで作成 (長さと容量を指定)
    s2 := make([]int, 3, 5) // 長さ3, 容量5のスライス [0 0 0]
    fmt.Println(s2, len(s2), cap(s2)) // [0 0 0] 3 5
    // appendで要素追加 (容量が足りなければ新しい配列を確保)
    s1 = append(s1, 4, 5)
    fmt.Println(s1, len(s1), cap(s1)) // [1 2 3 4 5] 5 6 (容量は実行環境依存で増加)
    // スライスからスライスを作成 (元の配列を共有)
    s3 := s1[1:3] // インデックス1から3の手前まで [2 3]
    fmt.Println(s3, len(s3), cap(s3)) // [2 3] 2 5 (容量は元のスライスの末尾まで)
    s3[0] = 99 // 元のスライスs1も変更される
    fmt.Println(s1) // [1 99 3 4 5]
    // copyでスライスをコピー
    s4 := make([]int, len(s1))
    copiedCount := copy(s4, s1)
    fmt.Println(s4, copiedCount) // [1 99 3 4 5] 5
  • マップ (Map): キーと値のペア
    // マップリテラルで作成
    m1 := map[string]int{"apple": 100, "banana": 200}
    fmt.Println(m1) // map[apple:100 banana:200]
    // makeで作成
    m2 := make(map[string]string)
    m2["go"] = "Golang"
    m2["js"] = "JavaScript"
    fmt.Println(m2) // map[go:Golang js:JavaScript]
    // 要素へのアクセスと存在確認
    price := m1["apple"] // 100
    price, ok := m1["orange"] // price = 0 (ゼロ値), ok = false
    if ok { fmt.Println("Price:", price)
    } else { fmt.Println("Orange not found")
    }
    // 要素の削除
    delete(m1, "banana")
    fmt.Println(m1) // map[apple:100]
    // マップの反復 (順序は保証されない)
    for key, value := range m2 { fmt.Printf("Key: %s, Value: %s\n", key, value)
    }
  • 構造体 (Struct): 複数のフィールドを持つカスタム型
    type Person struct { Name string Age int
    }
    // 構造体の初期化
    p1 := Person{Name: "Alice", Age: 30}
    p2 := Person{"Bob", 25} // フィールド名を省略 (順序が重要)
    p3 := new(Person) // ポインタを返す (*Person)、フィールドはゼロ値
    p3.Name = "Charlie"
    p3.Age = 40
    fmt.Println(p1, p2, *p3) // {Alice 30} {Bob 25} {Charlie 40}
    // フィールドへのアクセス
    fmt.Println(p1.Name) // Alice
    // 構造体の埋め込み (Embedding)
    type Employee struct { Person // 型名をそのままフィールドとして埋め込む EmployeeID string Salary int
    }
    emp := Employee{ Person: Person{Name: "David", Age: 35}, EmployeeID: "E123", Salary: 50000,
    }
    // 埋め込まれた構造体のフィールドに直接アクセス可能
    fmt.Println(emp.Name) // David
    fmt.Println(emp.Person.Age) // 35
  • ポインタの取得とデリファレンス:
    x := 10
    p := &x // xのアドレスをポインタpに格納
    fmt.Println("Value of x:", x) // 10
    fmt.Println("Address of x:", p) // メモリアドレス (例: 0x...)
    fmt.Println("Value via p:", *p) // ポインタpが指す先の値 (デリファレンス) -> 10
    *p = 20 // ポインタ経由で値を変更
    fmt.Println("New value of x:", x) // 20
  • 関数でのポインタ利用: 値渡しではなく参照渡しのような効果
    func increment(val *int) { *val++ // ポインタが指す先の値をインクリメント
    }
    num := 5
    increment(&num)
    fmt.Println("Incremented num:", num) // 6
  • new関数: 型のゼロ値へのポインタを割り当てて返す
    ptr := new(int) // int型のゼロ値(0)へのポインタを返す
    fmt.Println(*ptr) // 0
    *ptr = 100
    fmt.Println(*ptr) // 100
  • メソッド定義: 特定の型に関連付けられた関数
    type Rect struct { Width, Height float64
    }
    // 値レシーバ (元の値を変更しない)
    func (r Rect) Area() float64 { return r.Width * r.Height
    }
    // ポインタレシーバ (元の値を変更できる)
    func (r *Rect) Scale(factor float64) { r.Width *= factor r.Height *= factor
    }
    rect := Rect{Width: 10, Height: 5}
    fmt.Println("Area:", rect.Area()) // 50.0
    rect.Scale(2) // ポインタレシーバでも自動的にアドレスが渡される
    fmt.Println("Scaled Rect:", rect) // {20 10}
    // ポインタ型変数でもメソッド呼び出し可能
    rectPtr := &Rect{Width: 3, Height: 4}
    fmt.Println("Area:", rectPtr.Area()) // 12.0 (自動的にデリファレンスされる)
    rectPtr.Scale(3)
    fmt.Println("Scaled Rect Ptr:", *rectPtr) // {9 12}
  • インターフェース定義: メソッドシグネチャの集まり
    type Shape interface { Area() float64
    }
    type Circle struct { Radius float64
    }
    func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius
    }
    // Shapeインターフェースを満たす型はShape型の変数に代入可能
    func printArea(s Shape) { fmt.Printf("Area of %T: %f\n", s, s.Area())
    }
    r := Rect{Width: 4, Height: 5}
    c := Circle{Radius: 3}
    printArea(r) // Rect型はArea()メソッドを持つのでShapeインターフェースを満たす
    printArea(c) // Circle型も同様
  • 空インターフェース (`interface{}`): あらゆる型を保持できる。型アサーションや型スイッチで元の型を取り出す。
    var i interface{}
    i = "hello"
    fmt.Println(i) // hello
    i = 42
    fmt.Println(i) // 42
    // 型アサーション (Type Assertion)
    s, ok := i.(string) // string型への変換を試みる
    if ok { fmt.Println("It's a string:", s)
    } else { fmt.Println("Not a string")
    }
    // 型スイッチ (Type Switch)
    func checkType(v interface{}) { switch t := v.(type) { case string: fmt.Println("String:", t) case int: fmt.Println("Integer:", t) case bool: fmt.Println("Boolean:", t) default: fmt.Printf("Unknown type: %T\n", t) }
    }
    checkType("world")
    checkType(123)
    checkType(true)
    checkType(3.14)
  • Stringer インターフェース: `fmt`パッケージなどでオブジェクトを文字列として表現するために使われる。
    type IPAddr [4]byte
    // String()メソッドを実装することでStringerインターフェースを満たす
    func (ip IPAddr) String() string {	return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
    }
    addr := IPAddr{127, 0, 0, 1}
    fmt.Println(addr) // 127.0.0.1 (String()が呼ばれる)
  • `error` インターフェース: Goのエラー処理の基本。`Error()`メソッドを持つ。
    type error interface { Error() string
    }
  • 基本的なエラー処理: 関数の戻り値として`error`を返し、呼び出し側で`nil`チェックを行う。
    file, err := os.Open("nonexistent.txt")
    if err != nil { log.Printf("Failed to open file: %v\n", err) // エラーに応じた処理 (早期リターンなど) return
    }
    defer file.Close()
    // 正常系の処理
    fmt.Println("File opened successfully.")
  • `errors.New`: シンプルなエラーメッセージを持つ`error`を作成。
    err := errors.New("something went wrong")
    fmt.Println(err)
  • `fmt.Errorf`: フォーマット指定子を使ってエラーメッセージを組み立て、ラップされたエラー情報を含める。
    func readFile(path string) error { _, err := os.ReadFile(path) if err != nil { // %w で元のエラーをラップする return fmt.Errorf("error reading file %s: %w", path, err) } return nil
    }
    err := readFile("config.yaml")
    if err != nil { fmt.Println(err)
    }
  • `errors.Is`: エラーチェーンを辿り、特定のエラーが含まれているか確認。
    err1 := os.ErrNotExist // 事前定義されたエラー
    err2 := fmt.Errorf("operation failed: %w", err1)
    if errors.Is(err2, os.ErrNotExist) { fmt.Println("The file does not exist.")
    }
  • `errors.As`: エラーチェーンを辿り、特定の型のエラーが含まれているか確認し、その値を抽出。
    type MyError struct { Code int Msg string
    }
    func (e *MyError) Error() string { return fmt.Sprintf("Error %d: %s", e.Code, e.Msg)
    }
    func doSomething() error { // 何らかの処理... return fmt.Errorf("wrapping custom error: %w", &MyError{Code: 500, Msg: "internal server error"})
    }
    err := doSomething()
    var myErr *MyError
    if errors.As(err, &myErr) { fmt.Printf("Caught MyError - Code: %d, Message: %s\n", myErr.Code, myErr.Msg)
    } else { fmt.Println("Caught a different error:", err)
    }
  • `panic` と `recover`: 予期せぬ致命的なエラー(例: 配列の範囲外アクセス)で発生。`defer`と`recover`でパニックから回復可能だが、通常のエラー処理には使わないのがベストプラクティス。
    func recoveryDemo() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } }() fmt.Println("Calling dangerous function...") causePanic() fmt.Println("This line will not be executed if panic occurs and is recovered.")
    }
    func causePanic() { fmt.Println("About to panic!") panic("something bad happened") fmt.Println("This line will not be executed.")
    }
    recoveryDemo()
  • Goroutine (ゴルーチン): `go`キーワードで関数を非同期に実行。軽量なスレッドのようなもの。
    func say(s string) { for i := 0; i < 3; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) }
    }
    func main() { go say("World") // 新しいGoroutineでsay("World")を実行 say("Hello") // 現在のGoroutineでsay("Hello")を実行 // main関数が終了すると他のGoroutineも終了するため、少し待つ // time.Sleep(500 * time.Millisecond) // より良い方法はsync.WaitGroupを使う
    }
  • Channel (チャネル): Goroutine間でデータを送受信するためのパイプ。型を持つ。
    // チャネル作成 (バッファなし)
    ch := make(chan string)
    go func() { time.Sleep(1 * time.Second) ch <- "Data from goroutine" // チャネルにデータを送信 (受信されるまでブロック)
    }()
    fmt.Println("Waiting for data...")
    msg := <-ch // チャネルからデータを受信 (データが来るまでブロック)
    fmt.Println("Received:", msg)
    // バッファ付きチャネル (指定サイズまでブロックせずに送信可能)
    bufCh := make(chan int, 2)
    bufCh <- 1 // ブロックしない
    bufCh <- 2 // ブロックしない
    // bufCh <- 3 // バッファが満杯なのでブロックする
    fmt.Println(<-bufCh) // 1
    fmt.Println(<-bufCh) // 2
  • `select`: 複数のチャネル操作を待機し、最初に準備ができた操作を実行。
    c1 := make(chan string)
    c2 := make(chan string)
    go func() { time.Sleep(1 * time.Second) c1 <- "one"
    }()
    go func() { time.Sleep(2 * time.Second) c2 <- "two"
    }()
    // 2つのGoroutineからの応答を待つ
    for i := 0; i < 2; i++ { select { case msg1 := <-c1: fmt.Println("Received from c1:", msg1) case msg2 := <-c2: fmt.Println("Received from c2:", msg2) case <-time.After(3 * time.Second): // タイムアウト fmt.Println("Timeout waiting for messages") return // default: // 他に準備ができているチャネルがない場合に実行 (ノンブロッキング) // fmt.Println("No messages ready yet") // time.Sleep(100 * time.Millisecond) }
    }
  • `close`: チャネルを閉じる。送信側が行う。受信側はチャネルが閉じられたことを検知できる。
    jobs := make(chan int, 5)
    done := make(chan bool)
    go func() { for { j, more := <-jobs // moreはチャネルが開いているかを示す if more { fmt.Println("Received job", j) } else { fmt.Println("Received all jobs") done <- true return } }
    }()
    for j := 1; j <= 3; j++ { jobs <- j fmt.Println("Sent job", j)
    }
    close(jobs) // これ以上送信しないことを示す
    fmt.Println("Sent all jobs and closed channel")
    <-done // worker goroutineの終了を待つ
  • `sync.Mutex`: 複数のGoroutineから共有リソースへのアクセスを排他制御(ロック)。
    var counter int
    var mu sync.Mutex
    var wg sync.WaitGroup
    func incrementCounter() { defer wg.Done() for i := 0; i < 1000; i++ { mu.Lock() // ロックを取得 counter++ mu.Unlock() // ロックを解放 }
    }
    func main() { numGoroutines := 5 wg.Add(numGoroutines) for i := 0; i < numGoroutines; i++ { go incrementCounter() } wg.Wait() // 全てのGoroutineの終了を待つ fmt.Println("Final Counter:", counter) // 期待値: 5000
    }
  • `sync.WaitGroup`: 複数のGoroutineの終了を待機。
    // 上記Mutexの例を参照
    // 1. WaitGroup変数を宣言: var wg sync.WaitGroup
    // 2. Goroutine起動前に wg.Add(1) でカウンタを増やす
    // 3. Goroutine内の処理終了時に defer wg.Done() でカウンタを減らす
    // 4. メインGoroutineで wg.Wait() でカウンタが0になるのを待つ
  • `context`: リクエストスコープの値、キャンセル信号、デッドラインをGoroutine間で伝播させる。
    func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): // 親コンテキストからのキャンセル通知を待つ fmt.Printf("Worker %d: stopping due to cancellation: %v\n", id, ctx.Err()) return default: fmt.Printf("Worker %d: working...\n", id) time.Sleep(500 * time.Millisecond) // ここで ctx.Value("requestID") のような値を使うこともできる } }
    }
    func main() { // タイムアウト付きのコンテキストを作成 (例: 2秒後にキャンセル) // ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) // キャンセル関数付きのコンテキストを作成 ctx, cancel := context.WithCancel(context.Background()) defer cancel() // main関数終了時に必ずキャンセルを呼ぶ // リクエストIDなどの値を付与する場合 // requestID := "req-123" // ctx = context.WithValue(ctx, "requestID", requestID) go worker(ctx, 1) go worker(ctx, 2) // 3秒後にキャンセルシグナルを送る time.Sleep(3 * time.Second) fmt.Println("Main: sending cancellation signal...") cancel() // Workerが終了するのを少し待つ time.Sleep(1 * time.Second) fmt.Println("Main: finished.")
    }
  • **並行処理パターン例:**
    • **Generator:** チャネルを使って一連の値を生成する関数。
    • **Worker Pool:** 固定数のワーカーGoroutineでタスクキューを処理する。
    • **Pipeline:** 複数のステージ(Goroutine)をチャネルで繋ぎ、データを段階的に処理する。
    • **Fan-In/Fan-Out:** 複数のGoroutineにタスクを分散(Fan-Out)し、結果を一つのチャネルに集約(Fan-In)。
    • **Semaphore:** 同時にアクセスできるGoroutineの数を制限する。
  • モジュールの初期化: プロジェクトルートで実行。`go.mod`ファイルが生成される。
    go mod init example.com/myproject
  • 依存パッケージの追加: コード内で`import`し、`go build`や`go test`を実行すると自動的に`go.mod`と`go.sum`に追加される。明示的に追加・更新する場合は`go get`。
    # 最新バージョンを追加
    go get github.com/gin-gonic/gin
    # 特定のバージョンを追加
    go get github.com/gin-gonic/gin@v1.7.7
    # バージョンを更新
    go get -u github.com/gin-gonic/gin
    # 全ての依存関係を更新
    go get -u ./...
  • 依存関係の整理: `go.mod`を整理し、不要な依存を削除。`go.sum`も更新される。
    go mod tidy
  • 依存関係のダウンロード:
    go mod download
  • 依存関係の確認:
    go list -m all
  • ベンダーディレクトリの使用: 依存関係をプロジェクト内の`vendor`ディレクトリにコピー。ビルド時にここが参照される。
    go mod vendor
  • ファイル名と関数名の規約:
    • テストファイル: `*_test.go`
    • テスト関数: `func TestXxx(*testing.T)` (Xxxはテスト対象の関数名など、大文字で始まる)
    • ベンチマーク関数: `func BenchmarkXxx(*testing.B)`
    • Example関数: `func ExampleXxx()`
  • 基本的なテスト関数: `testing.T`を使ってエラー報告やテスト終了を行う。
    // mymath/mymath.go
    package mymath
    func Add(a, b int) int { return a + b
    }
    // mymath/mymath_test.go
    package mymath
    import "testing"
    func TestAdd(t *testing.T) { result := Add(2, 3) expected := 5 if result != expected { t.Errorf("Add(2, 3) = %d; want %d", result, expected) // エラーを報告しテスト続行 // t.Fatalf("Add(2, 3) = %d; want %d", result, expected) // エラーを報告しテスト終了 }
    }
  • テストの実行:
    # カレントディレクトリのテストを実行
    go test
    # 詳細表示 (-v)
    go test -v
    # 特定のテスト関数を実行 (-run 正規表現)
    go test -v -run TestAdd
    # カバレッジ計測
    go test -cover
    go test -coverprofile=coverage.out
    go tool cover -html=coverage.out # HTMLで結果表示
  • テーブル駆動テスト: 複数のテストケースをまとめて記述する一般的なパターン。
    func TestAddTableDriven(t *testing.T) { testCases := []struct { name string // テストケース名 (オプション) a, b int expected int }{ {"positive numbers", 2, 3, 5}, {"negative numbers", -1, -5, -6}, {"zero", 0, 0, 0}, {"positive and negative", 10, -3, 7}, } for _, tc := range testCases { // t.Run でサブテストを作成 (go test -run TestAddTableDriven/negative_numbers のように実行可能) t.Run(tc.name, func(t *testing.T) { result := Add(tc.a, tc.b) if result != tc.expected { t.Errorf("Add(%d, %d) = %d; want %d", tc.a, tc.b, result, tc.expected) } }) }
    }
  • ベンチマークテスト: コードのパフォーマンスを測定。
    func BenchmarkAdd(b *testing.B) { // b.N はベンチマークフレームワークによって自動的に調整される実行回数 for i := 0; i < b.N; i++ { Add(100, 200) }
    }
    # ベンチマーク実行 (-bench 正規表現)
    go test -bench .
    go test -bench BenchmarkAdd
  • Example関数: 関数の使用例を示し、godocに表示される。出力がコメントと一致するかテストされる。
    func ExampleAdd() { sum := Add(1, 5) fmt.Println(sum) // Output: 6
    }
  • ヘルパー関数とセットアップ/ティアダウン:
    • `t.Helper()`: テストヘルパー関数内で呼び出すと、エラー発生時のファイル/行番号がヘルパー呼び出し元になる。
    • `TestMain`: パッケージ内のテスト実行前後に一度だけ実行される特殊な関数。DB接続や一時ファイル作成などに使う。
      func TestMain(m *testing.M) { fmt.Println("Setting up tests...") // セットアップ処理 exitCode := m.Run() // パッケージ内のテストを実行 fmt.Println("Tearing down tests...") // ティアダウン処理 os.Exit(exitCode)
      }
  • HTTPテスト: `net/http/httptest`パッケージでHTTPハンドラやクライアントをテスト。
    func MyHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, test!")
    }
    func TestMyHandler(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/test", nil) rr := httptest.NewRecorder() // http.ResponseWriterのモック handler := http.HandlerFunc(MyHandler) handler.ServeHTTP(rr, req) // ステータスコードをチェック if status := rr.Code; status != http.StatusOK { t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) } // レスポンスボディをチェック expected := "Hello, test!\n" if rr.Body.String() != expected { t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) }
    }
  • モックとスタブ: 外部依存(DB, APIなど)をテストダブルに置き換える。インターフェースを活用すると容易になる。`gomock`などのライブラリも利用される。
目的パッケージ関数/メソッドコード例備考
ファイル全体を読み込む (推奨)`os``ReadFile`
data, err := os.ReadFile("myfile.txt")
if err != nil { /*...*/ }
fmt.Println(string(data))
Go 1.16+。シンプルでメモリ効率が良い。
ファイル全体を書き込む (推奨)`os``WriteFile`
data := []byte("Hello, Go!")
err := os.WriteFile("output.txt", data, 0644) // 0644はパーミッション
if err != nil { /*...*/ }
Go 1.16+。ファイルが存在しない場合は作成、存在する場合は上書き。
ファイルを開く (読み取り)`os``Open`
file, err := os.Open("input.txt")
if err != nil { /*...*/ }
defer file.Close()
// fileを使って読み込み処理
読み取り専用 (`O_RDONLY`)。`defer file.Close()`を忘れずに。
ファイルを作成/開く (書き込み)`os``Create`
file, err := os.Create("newfile.txt")
if err != nil { /*...*/ }
defer file.Close()
// fileを使って書き込み処理
書き込み用に開く (`O_WRONLY|O_CREATE|O_TRUNC`)。存在する場合は内容を切り捨てる。
ファイルを開く (詳細指定)`os``OpenFile`
flag := os.O_WRONLY|os.O_CREATE|os.O_APPEND
perm := 0666
file, err := os.OpenFile("logfile.log", flag, perm)
if err != nil { /*...*/ }
defer file.Close()
`flag`で開くモード (追記、読み書きなど)、`perm`でパーミッションを指定。
ファイルから読み込む (`*os.File`)`os``(*File).Read`
buf := make([]byte, 1024)
n, err := file.Read(buf)
if err != nil && err != io.EOF { /*...*/ }
fmt.Println(string(buf[:n]))
指定したバッファに読み込む。EOF (End Of File) はエラーではない場合がある。
ファイルへ書き込む (`*os.File`)`os``(*File).Write`
data := []byte("Some data\n")
n, err := file.Write(data)
if err != nil { /*...*/ }
fmt.Printf("Wrote %d bytes\n", n)
バイトスライスを書き込む。
ファイルへ文字列を書き込む (`*os.File`)`os``(*File).WriteString`
n, err := file.WriteString("Another line\n")
if err != nil { /*...*/ }
`Write([]byte(s))`の効率的な代替。
バッファリング読み込み (行単位など)`bufio``NewScanner`
scanner := bufio.NewScanner(file)
for scanner.Scan() { // 1行ずつ読み込む fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil { /*...*/ }
大きなファイルを効率的に処理。区切り文字の変更も可能。
バッファリング書き込み`bufio``NewWriter`
writer := bufio.NewWriter(file)
_, err := writer.WriteString("Buffered write.\n")
if err != nil { /*...*/ }
err = writer.Flush() // バッファの内容を書き出す
if err != nil { /*...*/ }
書き込み回数を減らし効率化。最後に`Flush()`が必要。
ファイル/ディレクトリの情報を取得`os``Stat`, `Lstat`
fileInfo, err := os.Stat("myfile.txt")
if err != nil { /*...*/ }
fmt.Println("Name:", fileInfo.Name())
fmt.Println("Size:", fileInfo.Size())
fmt.Println("Is Dir:", fileInfo.IsDir())
fmt.Println("Mode:", fileInfo.Mode())
fmt.Println("ModTime:", fileInfo.ModTime())
`Lstat`はシンボリックリンク自体の情報を返す。
ファイル/ディレクトリの存在確認`os``Stat`, `IsNotExist`
_, err := os.Stat("check.txt")
if os.IsNotExist(err) { fmt.Println("File does not exist.")
} else if err != nil { // その他のエラー (権限など) /*...*/
} else { fmt.Println("File exists.")
}
`Stat`のエラーを`errors.Is(err, os.ErrNotExist)`でチェックするのも一般的。
ディレクトリ作成`os``Mkdir`, `MkdirAll`
// 単一ディレクトリ作成
err := os.Mkdir("newdir", 0755)
// 中間ディレクトリもまとめて作成
err = os.MkdirAll("path/to/deep/dir", 0755)
`MkdirAll`は存在していてもエラーにならない。
ファイル/ディレクトリの削除`os``Remove`, `RemoveAll`
// ファイルまたは空のディレクトリを削除
err := os.Remove("file_to_delete.txt")
// ディレクトリとその中身を再帰的に削除
err = os.RemoveAll("dir_to_delete")
`RemoveAll`は慎重に使用。
ファイル/ディレクトリのリネーム/移動`os``Rename`
err := os.Rename("oldname.txt", "newname.txt")
err = os.Rename("file.txt", "archive/file.txt")
同じファイルシステム内でのみ動作。
  • Goの構造体 → JSON文字列 (エンコード/マーシャリング): `encoding/json.Marshal`
    type Config struct { Server string `json:"serverAddress"` // タグでJSONキー名を指定 Port int `json:"port"` Enabled bool `json:"enabled"` Users []string `json:"users,omitempty"` // omitempty: スライスがnilか空なら出力しない Password string `json:"-"` // -: このフィールドはJSONに含めない
    }
    conf := Config{ Server: "localhost", Port: 8080, Enabled: true, // Users: []string{"admin", "guest"}, Password: "secret",
    }
    jsonData, err := json.Marshal(conf)
    // jsonData, err := json.MarshalIndent(conf, "", " ") // 整形して出力する場合
    if err != nil { log.Fatal("JSON marshal error:", err)
    }
    fmt.Println(string(jsonData))
    // 出力例 (Marshal): {"serverAddress":"localhost","port":8080,"enabled":true}
    // 出力例 (MarshalIndent):
    // {
    // "serverAddress": "localhost",
    // "port": 8080,
    // "enabled": true
    // }
  • JSON文字列 → Goの構造体 (デコード/アンマーシャリング): `encoding/json.Unmarshal`
    jsonString := `{"serverAddress":"192.168.1.100","port":9000,"enabled":false,"users":["root"],"unknownField":"ignore"}`
    var loadedConf Config
    err := json.Unmarshal([]byte(jsonString), &loadedConf) // ポインタを渡す
    if err != nil { log.Fatal("JSON unmarshal error:", err)
    }
    fmt.Printf("Loaded Config: %+v\n", loadedConf)
    // 出力例: Loaded Config: {Server:192.168.1.100 Port:9000 Enabled:false Users:[root] Password:}
  • JSON文字列 → 不明な構造 (`map[string]interface{}`):
    jsonString := `{"name":"Alice","age":30,"city":"New York","extra":{"active":true}}`
    var data map[string]interface{} // mapまたはinterface{}にデコード
    err := json.Unmarshal([]byte(jsonString), &data)
    if err != nil { /*...*/ }
    name := data["name"].(string) // 型アサーションで値を取り出す
    age := data["age"].(float64) // JSONの数値はfloat64としてデコードされることが多い
    extraMap := data["extra"].(map[string]interface{})
    isActive := extraMap["active"].(bool)
    fmt.Println(name, age, isActive) // Alice 30 true
  • ストリーミングデコード (大きなJSONやリクエストボディ): `json.Decoder`
    // http.Request.Body (io.Reader) などから読み込む場合
    // dec := json.NewDecoder(r.Body)
    // err := dec.Decode(&myStruct)
    // ファイルから読み込む例
    file, _ := os.Open("large.json")
    defer file.Close()
    dec := json.NewDecoder(file) // io.Readerを渡す
    var v map[string]interface{} // または具体的な構造体
    err := dec.Decode(&v) // JSONオブジェクトを1つデコード
    if err != nil { /*...*/ }
    fmt.Println(v)
  • ストリーミングエンコード: `json.Encoder`
    // http.ResponseWriter (io.Writer) などに書き込む場合
    // enc := json.NewEncoder(w)
    // err := enc.Encode(myStruct)
    // ファイルに書き込む例
    file, _ := os.Create("output_stream.json")
    defer file.Close()
    enc := json.NewEncoder(file) // io.Writerを渡す
    enc.SetIndent("", " ") // 整形出力する場合
    data := map[string]string{"hello": "world"}
    err := enc.Encode(data) // JSONオブジェクトを書き込む
    if err != nil { /*...*/ }
  • シンプルなHTTP GETリクエスト: `net/http.Get`
    resp, err := http.Get("https://example.com")
    if err != nil { log.Fatal("HTTP GET error:", err)
    }
    defer resp.Body.Close() // レスポンスボディは必ず閉じる
    fmt.Println("Status Code:", resp.StatusCode)
    if resp.StatusCode == http.StatusOK { bodyBytes, err := io.ReadAll(resp.Body) if err != nil { log.Fatal("Error reading response body:", err) } fmt.Println("Response Body:\n", string(bodyBytes))
    }
  • HTTPリクエストのカスタマイズ (`http.Client`, `http.NewRequest`):
    // カスタムクライアント (タイムアウト設定など)
    client := &http.Client{ Timeout: 10 * time.Second,
    }
    // リクエストを作成 (メソッド, URL, ボディを指定)
    reqBody := strings.NewReader(`{"key":"value"}`) // io.Reader
    req, err := http.NewRequest(http.MethodPost, "https://httpbin.org/post", reqBody)
    if err != nil { log.Fatal("Error creating request:", err)
    }
    // ヘッダーを設定
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Authorization", "Bearer your_token")
    req.Header.Set("User-Agent", "MyGoApp/1.0")
    // リクエストを実行
    resp, err := client.Do(req)
    if err != nil { log.Fatal("HTTP Do error:", err)
    }
    defer resp.Body.Close()
    // レスポンス処理 (上記Getの例と同様)
    fmt.Println("Status Code:", resp.StatusCode)
    bodyBytes, _ := io.ReadAll(resp.Body)
    fmt.Println(string(bodyBytes))
  • シンプルなHTTPサーバー: `net/http.ListenAndServe`, `http.HandleFunc`
    func helloHandler(w http.ResponseWriter, r *http.Request) { // w: レスポンスを書き込むためのライター // r: リクエスト情報を持つポインタ fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) // URLパスから名前を取得 (例: /Alice -> Hello, Alice!)
    }
    func headersHandler(w http.ResponseWriter, r *http.Request) { for name, headers := range r.Header { for _, h := range headers { fmt.Fprintf(w, "%v: %v\n", name, h) } }
    }
    func main() { // URLパスとハンドラ関数を登録 http.HandleFunc("/hello/", helloHandler) http.HandleFunc("/headers", headersHandler) fmt.Println("Starting server on :8080") // サーバーを起動 (アドレスとデフォルトのServeMuxを指定) err := http.ListenAndServe(":8080", nil) if err != nil { log.Fatal("ListenAndServe error: ", err) }
    }
  • HTTPサーバーのカスタマイズ (`http.Server`): タイムアウト設定など詳細な制御。
    func main() { mux := http.NewServeMux() // 新しいServeMuxを作成 mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Custom Server Root") }) server := &http.Server{ Addr: ":8081", Handler: mux, // カスタムServeMuxを指定 ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, IdleTimeout: 120 * time.Second, } fmt.Println("Starting custom server on :8081") err := server.ListenAndServe() if err != nil && err != http.ErrServerClosed { log.Fatal("Server error: ", err) }
    }
  • TCPサーバー (低レベル): `net.Listen`, `listener.Accept`
    func handleConnection(conn net.Conn) { defer conn.Close() fmt.Printf("Accepted connection from %s\n", conn.RemoteAddr()) buf := make([]byte, 1024) for { n, err := conn.Read(buf) if err != nil { if err != io.EOF { fmt.Println("Read error:", err) } break // EOF またはエラーでループ終了 } fmt.Printf("Received: %s", string(buf[:n])) // エコーバック _, err = conn.Write([]byte("Echo: " + string(buf[:n]))) if err != nil { fmt.Println("Write error:", err) break } } fmt.Printf("Connection closed from %s\n", conn.RemoteAddr())
    }
    func main() { listener, err := net.Listen("tcp", ":8888") if err != nil { log.Fatal("Listen error:", err) } defer listener.Close() fmt.Println("TCP Server listening on :8888") for { conn, err := listener.Accept() // 新しい接続を待機 if err != nil { fmt.Println("Accept error:", err) continue // エラーが発生しても次の接続を待つ } go handleConnection(conn) // 各接続を個別のGoroutineで処理 }
    }
  • TCPクライアント (低レベル): `net.Dial`
    func main() { conn, err := net.Dial("tcp", "localhost:8888") if err != nil { log.Fatal("Dial error:", err) } defer conn.Close() fmt.Println("Connected to server.") // サーバーにメッセージを送信 _, err = conn.Write([]byte("Hello from client!\n")) if err != nil { log.Fatal("Write error:", err) } // サーバーからの応答を受信 buf := make([]byte, 1024) n, err := conn.Read(buf) if err != nil { log.Fatal("Read error:", err) } fmt.Printf("Server response: %s", string(buf[:n]))
    }

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です