Go言語のクロージャとは?関数内関数の活用例と仕組み
生徒
「Go言語っていうプログラミングで、クロージャって聞いたんですけど、何なんですか?」
先生
「いい質問ですね。クロージャとは、関数の中で作られた関数のことです。Go言語では関数を他の関数の中で定義して、記憶させることができるんですよ。」
生徒
「えっ?関数の中に関数があるんですか?それって何か便利なんですか?」
先生
「とっても便利ですよ!じゃあ、仕組みと使い方を具体的に見ていきましょう!」
1. クロージャとは?基本の考え方を理解しよう
Go言語(Golang)のクロージャとは、関数の中で定義された関数が、外側の関数の変数(環境)を記憶して持ち運べる仕組みのことです。外側で用意した値や状態を、内側の関数が後から参照・更新できます。これは関数を値として扱えるGo言語の特性とセットで理解すると分かりやすく、短いコードで「状態を覚える処理」を書けるのが特徴です。
身近な例に置き換えると、台所の蛇口(内側の関数)は、どの水道管(外側の変数)につながっているかを覚えています。蛇口をひねるたびに、同じ水道管から水(前回の状態を引き継いだ値)が流れてくる、というイメージです。
次の超短い例では、外側で用意した回数カウンタを、内側の関数が覚えて増やし続けます。ここでは「関数が外側の変数をキャプチャして記憶する」点だけを体験できれば十分です。
package main
import "fmt"
func main() {
counter := func() func() int {
n := 0
return func() int {
n++
return n
}
}()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2
}
このコードでは、内側の無名関数が外側の変数nを覚えています。counter()を呼ぶたびにnが増え、前回の値が次回へ引き継がれます。これがGo言語のクロージャの基本です。キーワードは「関数の内部関数」「外側の変数をキャプチャ」「状態を保つ」「あとから呼んでも覚えている」です。
2. クロージャを使った簡単な例
ここでは、Go言語のクロージャ(関数内関数)が「外側の変数を覚えて使い続ける」様子を、最小のコードで体験します。ポイントは、戻り値として返した無名関数が、外側スコープの変数をキャプチャして、次の呼び出しでも同じ状態を参照できることです。
package main
import "fmt"
func main() {
adder := createAdder() // 関数を作って受け取る(状態を持つ関数)
fmt.Println(adder(5)) // 5
fmt.Println(adder(3)) // 8(前回の合計に3を加算)
fmt.Println(adder(10)) // 18(合計を覚えている)
}
// 合計を覚えるクロージャを返すファクトリ関数
func createAdder() func(int) int {
sum := 0 // 外側の変数(状態)
return func(x int) int { // 無名関数がsumをキャプチャ
sum += x
return sum
}
}
上のコードでは、createAdderが「合計を覚える関数」を返し、返された関数はsumという変数を記憶したまま動きます。呼び出すたびにsumが更新されるため、状態を維持しながら計算を続けることができます。これは、カウンタやスコア加算、累積合計などの処理にそのまま応用できます。
初心者の方は、次の観点で読み解くと理解しやすくなります。
- 関数を変数に代入できる(
adderは「関数」という値) - 返された無名関数が外側の変数(
sum)を覚える(キャプチャ) - 呼び出すたびに同じ状態が使われるので結果が積み上がる
このように、Go言語のクロージャは「簡潔なコードで状態を扱う」ための強力な道具です。キーワードは無名関数・変数キャプチャ・状態保持・関数を返すです。
3. 実行結果を確認してみよう
上のプログラムを実行すると、ターミナル(またはVSCodeのターミナル)に次のような出力が順番に表示されます。数値が呼び出しごとに増えている点がポイントです。
5
8
18
最初の呼び出しでは合計が「5」、次に「5」に「3」を足して「8」、さらに「8」に「10」を足して「18」と表示されます。これは、クロージャが外側にある変数sumの現在値を記憶して更新し続けるためです。つまり、関数呼び出しのたびに計算がリセットされるのではなく、前回の結果が引き継がれるため、累積合計(カウンタ)として動作します。
確認時のコツとして、出力が想定どおり進んでいるかを一行ずつ追いかけてみましょう。もし値が増えない、または不自然に飛ぶ場合は、呼び出しの順序や数、実行しているファイルが正しいかを見直すと原因を特定しやすくなります。Go言語のクロージャは状態を持つため、スコア加算・合計金額・回数のカウントなど、実行結果の変化を追いかける用途に向いています。
また、同じ手順をもう一度繰り返しても、前回と同じ順序で呼び出せば同じ結果になります。これは再現性が高いという意味で、初心者が動作確認をする際にとても助かります。数字が積み上がる様子を意識しながら、出力とロジックのつながりを確かめてみてください。
4. なぜクロージャが便利なの?
クロージャを使うと、「ある状態を覚えておく関数」を作ることができます。普通の関数は呼び出すたびに最初から処理を行いますが、クロージャは外側の変数を記憶し、次の呼び出しでもその値を使い続けることができます。まるでメモ帳のように前回の結果を残しておけるので、繰り返し計算やカウンターのような処理に向いています。
たとえば、ゲームの得点を足し続ける場面を考えてみましょう。毎回変数を渡して計算するのではなく、クロージャなら得点の合計を内側で覚えてくれるため、呼び出すだけで合計が更新されていきます。
package main
import "fmt"
func main() {
score := createScoreCounter()
fmt.Println(score(100)) // 100
fmt.Println(score(50)) // 150
fmt.Println(score(25)) // 175
}
func createScoreCounter() func(int) int {
total := 0
return func(point int) int {
total += point
return total
}
}
このように、クロージャは「前の値を引き継いで再利用できる関数」を作るために役立ちます。特定の条件で動きを変える処理や、状態を持ちながら動作する関数を作りたいときに、とても便利な仕組みです。
5. 関数を変数のように扱えるGo言語の特性
Go言語では、関数そのものを値として変数に代入できます。数値や文字列と同じ感覚で「代入・引数に渡す・戻り値として返す」ができるので、処理を部品化して組み立てるときにとても強力です。クロージャもこの性質の上に成り立っており、関数を持ち運ぶからこそ「覚えた状態」をどこでも再利用できます。
まずは関数を変数に入れて呼び出す最小例です。仕組みがつかめれば、処理の切り出しや共通化、テストのしやすさが一気に向上します。
package main
import "fmt"
// 関数を変数に代入して使う基本例
func main() {
greeter := func(name string) string {
return "こんにちは、" + name
}
fmt.Println(greeter("Gopher"))
fmt.Println(greeter("Go学習者"))
}
この例では、greeterという変数に無名関数を代入し、普通の関数と同じように呼び出しています。次は「関数を返す」パターンです。戻り値が関数になることで、あとから呼ぶ場所ごとに振る舞いを差し替えたり、メッセージの前置きや後置きを自在に組み替えられます。
package main
import "fmt"
// 関数を返す:用途ごとに挨拶の前置きを切り替える
func main() {
newGreeter := func(prefix string) func(string) string {
return func(name string) string {
return prefix + "、" + name
}
}
hello := newGreeter("こんにちは")
goodEvening := newGreeter("こんばんは")
fmt.Println(hello("Gopher"))
fmt.Println(goodEvening("Go学習者"))
}
ポイントは次の三つです。
- 代入できる:関数を変数に入れて扱える(読みやすい小さな部品になる)
- 渡せる:関数を引数に渡して処理を差し替えられる(条件分岐がすっきり)
- 返せる:関数を戻り値にして設定や状態を閉じ込められる(クロージャの土台)
この「関数は値」という特性に慣れると、フィルタ処理、並行処理のコールバック、入力検証などでコードの重複を減らし、読みやすく保守しやすいGoらしい設計に近づけます。まずは小さな無名関数を変数に代入して使うところから始め、必要に応じて「渡す」「返す」へ広げてみましょう。
6. 複数のクロージャを作るとどうなる?
では、createAdderをもう一度呼び出したらどうなるでしょうか?実験してみましょう。
package main
import "fmt"
func main() {
adder1 := createAdder()
adder2 := createAdder()
fmt.Println(adder1(1)) // 出力: 1
fmt.Println(adder1(2)) // 出力: 3
fmt.Println(adder2(10)) // 出力: 10
fmt.Println(adder2(20)) // 出力: 30
}
func createAdder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
結果は以下のようになります:
1
3
10
30
adder1とadder2は別々のsumを持っていて、お互いの影響を受けません。これが状態を持つ関数の力です。
7. クロージャが使われる場面とは?
クロージャは、次のような場面でよく使われます:
- 何かの処理を段階的に進めたいとき(状態を記憶)
- 条件に応じて関数の動きを変えたいとき
- 関数を引数として渡すときに、その関数に特定の値を記憶させておきたいとき
たとえばゲームで「スコアを足していく処理」や「買い物の合計金額を覚えていく処理」などにも使えます。
まとめ
この記事ではGo言語におけるクロージャについて、その仕組みと活用方法をやさしく解説しました。クロージャは、外側の関数の変数を覚えて利用できる関数内関数のことで、Go言語の大きな特徴の一つです。通常の関数は呼び出すたびに新しい処理を行いますが、クロージャは「状態を保持し続けること」ができます。そのため、累積計算、状態管理、イベント処理、カスタマイズされた処理の作成など、実践的なプログラミングで非常に役立ちます。
さらに、クロージャはGo言語の「関数も値として扱える」という特性と組み合わさることで、柔軟かつ強力なコード設計を可能にします。特にWeb開発やAPI設計、ゲーム開発など多様な場面で利用され、コードの再利用性や拡張性を高めます。たとえば「カウンター機能」や「リスナー関数」などは典型的なクロージャの活用例です。
以下に、もう一度整理したサンプルコードを載せておきます。クロージャが変数を記憶して、呼び出しのたびに異なる結果を返す様子を確認できます。
package main
import "fmt"
func main() {
counter := createCounter()
fmt.Println(counter()) // 出力: 1
fmt.Println(counter()) // 出力: 2
fmt.Println(counter()) // 出力: 3
}
func createCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
この例ではcreateCounterが新しい関数を返し、その関数はcountという外側の変数を覚えています。呼び出すたびにcountが増えていくのは、まさにクロージャの特徴です。このようにクロージャを活用することで、Go言語プログラミングでは状態を扱いやすく、シンプルで明快なコードを実現できます。
生徒
「先生、今日の記事でクロージャのことがよく分かりました!Go言語では関数の中に関数を作れて、その関数が変数を覚えてくれるんですよね?」
先生
「その通りです。外側の変数を覚える関数、それがクロージャです。例えばカウンターを作るときや、計算の途中経過を保持したいときにとても便利ですね。」
生徒
「なるほど!普通の関数は呼ぶたびにリセットされるけど、クロージャなら前の状態を持ったまま次に進めるんですね。これならスコア計算や買い物の合計金額を覚えておく処理にも使えそうです!」
先生
「そうですね。Go言語では関数を値として扱えるので、クロージャと組み合わせるとプログラミングの幅がぐっと広がります。Webアプリやゲーム開発、サーバーサイドのAPI処理でもよく使われていますよ。」
生徒
「わあ、すごい!実際にサンプルプログラムを動かしてみたら、呼び出すたびに数字が増えていくのが本当に面白かったです。Go言語のクロージャをもっと練習して、自分のプログラムにも使ってみたいです!」
先生
「素晴らしい意欲ですね。クロージャをしっかり理解すれば、より高度なGo言語プログラミングにも自信を持って取り組めるようになりますよ。」
この記事を読んだ人からの質問
プログラミング初心者からのよくある疑問/質問を解決します
Go言語のクロージャとは何ですか?初心者でも理解できる簡単な説明が知りたいです。
Go言語のクロージャとは、関数の中で作られた関数が、外側の関数の変数を覚えて使い続ける仕組みのことです。普通の関数は呼び出すたびに初期化されますが、クロージャは前回の状態を残したまま処理を進められます。そのため、累積計算や状態管理が簡単にできる便利な機能です。
なぜGo言語のクロージャは「関数内関数」と呼ばれるのですか?
Go言語のクロージャは、関数の中で別の関数を定義して、その関数を返す仕組みだからです。外側の関数で定義した変数を、内側の関数が記憶するため「関数内関数」という表現がよく使われます。
クロージャは普通の関数と何が違うのですか?
普通の関数は処理が終わると変数の値が消えます。しかしクロージャは、外側の変数を覚えているので、同じ関数を何度呼んでも前回の結果を引き継げます。これが大きな違いで、累積カウンターや合計金額の計算などに向いています。
【超入門】ゼロから始めるGo言語プログラミング:最速で「動くアプリ」を作るマンツーマン指導
「プログラミングの仕組み」が根本からわかる。Go言語でバックエンド開発の第一歩を。
本講座を受講することで、単なる文法の暗記ではなく、「プログラムがコンピュータの中でどう動いているか」という本質的な理解につながります。シンプルながら強力なGo言語(Golang)を通じて、現代のバックエンドエンジニアに求められる基礎体力を最短距離で身につけます。
具体的な開発内容と環境
【つくるもの】
ターミナル(黒い画面)上で動作する「対話型計算プログラム」や、データを整理して表示する「ミニ・ツール」をゼロから作成します。自分の書いたコードが形になる感動を体験してください。
【開発環境】
プロの現場でシェアNo.1のVisual Studio Code (VS Code)を使用します。インストールから日本語化、Go言語用の拡張機能設定まで、現場基準の環境を一緒に構築します。
この60分で得られる3つの理解
「なぜ動くのか」という設定の仕組みを理解し、今後の独学で詰まらない土台を作ります。
データの種類やメモリの概念など、他言語にも通じるプログラミングの本質を学びます。
ただ動くだけでなく、誰が見ても分かりやすい「綺麗なコード」を書くための考え方を伝授します。
※本講座は、将来的にバックエンドエンジニアやクラウドインフラに興味がある未経験者のためのエントリー講座です。マンツーマン形式により、あなたの理解度に合わせて進行します。
初めてのGo言語を一緒に学びましょう!