1. 3件のとき
  2. 1件のとき
  3. 0が混ざるとき
  4. 順番が保たれるか
コース一覧
Go入門
待ち合わせ

Go入門

変数とゼロ値、Go唯一のループであるfor、複数戻り値とエラー処理、スライスとマップ、構造体とメソッド、ゴルーチンによる並行処理、net/httpによるAPI開発まで、Goの基礎を、毎レッスン実際にコードを書き、フリマアプリのAPIをGoで作りながら学びます。

1
Hello, Go
01. Goとは15分
02. 実行してみる15分
03. packageとmain15分
04. fmtで出力する15分
05. go buildと1バイナリ15分
06. つくる: 起動バナー20分
07. 第1章クイズ10分
2
変数と型
01. varで宣言する15分
02. := の短縮宣言15分
03. ゼロ値(Goならでは)15分
04. 基本の型15分
05. 暗黙変換なし15分
06. 定数とiota15分
07. つくる: 商品データ表示20分
08. 第2章クイズ10分
3
制御構文(forしかない世界)
01. Goのif15分
02. forしかない世界15分
03. forの七変化15分
04. Goのswitch15分
05. 定番: FizzBuzz15分
06. ループで集計15分
07. つくる: 商品集計20分
08. 第3章クイズ10分
4
関数とエラー処理
01. funcで関数を作る15分
02. 複数戻り値(Goならでは)15分
03. if err != nil(Goの心臓)15分
04. エラーを作る15分
05. deferで後始末15分
06. つくる: 検証つき関数群20分
07. 第4章クイズ10分
5
スライスとマップ
01. 配列15分
02. スライス15分
03. appendで育てる15分
04. rangeで走査する15分
05. マップ15分
06. カンマokイディオム15分
07. つくる: 検索と集計20分
08. 第5章クイズ10分
6
構造体とメソッド
01. 構造体15分
02. 初期化とフィールド15分
03. メソッド15分
04. ポインタレシーバ20分
05. New関数の慣習15分
06. 構造体のスライス15分
07. つくる: Itemで再設計20分
08. 第6章クイズ10分
7
ゴルーチンと並行処理のさわり
01. ゴルーチン15分
02. チャネル20分
03. 待ち合わせ20分
04. 並行の落とし穴20分
05. つくる: 並行集計20分
06. 第7章クイズ10分
8
Web API(net/http)
01. サーバーを立てる20分
02. ハンドラ関数20分
03. JSONを返す20分
04. ルーティング20分
05. 一覧API20分
06. 詳細APIと40420分
07. 出品API20分
08. つくる: API主要部完成25分
09. 第8章クイズ10分
9
総合制作
01. 仕様互換を仕上げる25分
02. Node版と差し替える25分
03. 1バイナリでデプロイ25分
04. 自由拡張25分
05. 完成と次のステップ20分

待ち合わせ

数を数えずに待つ

チャネルで待つには、入れた数と取り出す数を合わせる必要がありました。処理が増えると、この数合わせが面倒になります。

sync.WaitGroup は、数を数える係を用意してくれます。

Go

import "sync" var wg sync.WaitGroup for _, item := range items { wg.Add(1) go func() { defer wg.Done() process(item) }() } wg.Wait() // 全部終わるまでここで待つ

使うのは3つだけです。

プレーンテキスト

Add(1) これから1つ始める、と数を増やす Done() 1つ終わった、と数を減らす Wait() 数が0になるまで待つ

defer と組み合わせる

Go

go func() { defer wg.Done() // 何をしても、抜けるときに必ず Done が呼ばれる }()

第4章でやった defer がここで効きます。処理の途中で return しても、Done の呼び忘れが起こりません。

Done を呼び忘れると Wait が永久に終わりません。 defer で書くのは、そのための保険です。

Add はゴルーチンの外で

Go

for _, item := range items { wg.Add(1) // 外。正しい go func() { // wg.Add(1) をここに書くと間違い defer wg.Done() }() } wg.Wait()

中に書くと、Add が実行される前に Wait に到達することがあります。数が0のままなので、待たずに素通りします。

始まる前に数える。これが Add を外に置く理由です。

結果をどう受け取るか

WaitGroup は完了を待つだけで、値は運びません。結果が要るなら、別に用意します。

Go

results := make([]int, len(items)) for i, item := range items { wg.Add(1) go func(idx int, it Item) { defer wg.Done() results[idx] = process(it) // 添字が違うので衝突しない }(i, item) } wg.Wait()

スライスの別々の場所に書くので、同時に書いても問題ありません。同じ変数に足し込むと壊れます。それが次のレッスンの話です。

引数で渡す理由

Go

go func(idx int, it Item) { ... }(i, item)

ループの変数をそのまま使わず、引数で渡しています。第4章の defer で「引数はその場で確定する」と書いたのと同じ理由です。

Go 1.21 までは、ゴルーチンが動く頃にはループが先に進んで変数が変わっている、という有名な落とし穴がありました。Go 1.22 でループ変数は反復ごとに新しく作られるようになり、この問題は起きなくなっています。

それでも引数で渡す書き方を勧めるのは、何をゴルーチンに渡したのかが読んで分かるからです。外側の変数を暗黙に掴むより、渡すものを明示するほうが後から追えます。

要件

  1. 各要素の計算をゴルーチンで走らせる
  2. sync.WaitGroup で全部の完了を待つ
  3. Add はゴルーチンの外で呼ぶ
  4. Done は defer で呼ぶ
  5. 結果を元の順番のままカンマ区切りで返す

入出力例

gatherAll([2,3,4]) → "4,9,16" gatherAll([5]) → "25" gatherAll([0,3]) → "0,9" gatherAll([1,2,3,4]) → "1,4,9,16"

ヒント

初期コードはゴルーチンを使わず素直に計算しています。`wg.Add(1)` と `go func(...)` の形に書き換えてください

`go func(idx int, v int) { defer wg.Done(); results[idx] = v * v }(i, n)` の形です。引数で渡すのを忘れないでください

`wg.Wait()` をループの後に置きます。これが無いと結果が揃う前に文字列を組み立ててしまいます

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/08/10

関連レッスン

  • 並行の落とし穴

    同じ変数を複数のゴルーチンが触ると壊れることを知り、Mutexで守れるようになります。

  • つくる: 並行集計

    第7章の知識でカテゴリ別の集計を並行に実行し、結果を安全にまとめられるようになります。

  • 第7章クイズ

    ゴルーチン、チャネル、WaitGroup、データ競合についての理解を確認します。

  • サーバーを立てる

    net/httpでHTTPサーバーを立て、リクエストに応答を返せるようになります。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • 処理計算や代入を表す長方形
  • スライス範囲指定で部分列を取り出す記法
  • 変数データに名前をつけて参照する仕組み
  • 引数位置引数=順番で渡す。
  • ループ繰り返し処理。矢印で戻すか専用記号で示す
  • 反復色やフォントを繰り返して秩序を生む
main.go
学習モード
エディタを読み込んでいます

メモ

待ち合わせ

⌘S で保存