Go言語の関数のデバッグ方法!よくあるバグと対処法
生徒
「関数の中でバグが出たらどうやって見つければいいですか?」
先生
「まずはログ出力やfmt.Printlnで「見える化」するのが基本ですよ。」
生徒
「ログって何ですか?」
先生
「「ログ」とは実行中の状態を記録するもので、後から振り返って原因を探すために使います。」
生徒
「なるほど。実際に試してみたいです!」
先生
「それでは、関数のデバッグ技を順に見ていきましょう。」
1. fmt.Printlnによるログ出力で状態確認
fmt.Println()は、Go言語の関数デバッグで一番とりかかりやすい基本ワザです。関数の「入り口」「途中」「出口」で値を表示すると、どこで想定外になったかがすぐ分かります。まずは「入力」「計算途中の値」「戻り値」を順番に出して、バグの再現ポイントを絞り込みましょう。
package main
import "fmt"
// 2つの数を足し算する関数(初心者向けサンプル)
func Add(a, b int) int {
fmt.Println("[Add] start: a=", a, " b=", b) // 入り口で入力値を表示
sum := a + b
fmt.Println("[Add] mid : sum=", sum) // 途中の計算結果を表示
fmt.Println("[Add] end : return", sum) // 出口で戻り値を表示
return sum
}
func main() {
// 想定外の値が混ざっていないかを確認するための呼び出し
result := Add(2, 3)
fmt.Println("[main] 結果:", result)
}
[Add] start: a= 2 b= 3
[Add] mid : sum= 5
[Add] end : return 5
[main] 結果: 5
表示位置をそろえると読みやすく、関数の流れ(開始→途中→終了)が頭に入りやすくなります。必要な情報だけを短く出すのがコツです。調査が終わったら、ログの出力行は削除またはコメントアウトしておきましょう(ログの出しっぱなしは、他の問題を見えにくくします)。
2. logパッケージで正式なログ管理
logパッケージは、fmt.Printlnより一歩進んだ「時刻付きの記録」を簡単に残せます。log.SetPrefixでモジュール名を付け、log.SetFlagsで日時やファイル行番号を足すと、いつ・どこで・何が起きたかがすぐ追えます。まずは「時刻+行番号」を基本にしましょう。
package main
import (
"log"
)
func init() {
// ログの頭にタグを付け、時刻と行番号を出す
log.SetPrefix("[calc] ")
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}
// 2つの数を割り算して、処理の流れを記録するサンプル
func Divide(a, b float64) float64 {
log.Printf("start Divide a=%.2f b=%.2f", a, b) // 開始時の入力
// ※ここでは挙動観察のため、あえてそのまま割ります
ans := a / b
log.Printf("end Divide ans=%.2f", ans) // 終了時の結果
return ans
}
func main() {
r1 := Divide(10, 2)
log.Println("結果:", r1)
r2 := Divide(10, 0) // 0割り時の挙動もログで確認できる
log.Println("結果:", r2)
}
[calc] 2025/11/08 10:00:00 main.go:17: start Divide a=10.00 b=2.00
[calc] 2025/11/08 10:00:00 main.go:20: end Divide ans=5.00
[calc] 2025/11/08 10:00:00 main.go:26: 結果: 5
[calc] 2025/11/08 10:00:00 main.go:17: start Divide a=10.00 b=0.00
[calc] 2025/11/08 10:00:00 main.go:20: end Divide ans=+Inf
[calc] 2025/11/08 10:00:00 main.go:28: 結果: +Inf
この例では[calc]という接頭辞・日付・時刻・Lshortfileによるmain.go:行番号が並び、原因調査の「手がかり」が揃います。値はPrintlnよりフォーマット可能なPrintfで揃えると読みやすくなります。まずは「開始→終了」を必ず記録し、必要に応じて重要な変数だけを短く残すのがコツです。
3. if文でエラー回避する基本
ゼロ除算など事前にバグを防ぐための書き方は、入力値を検証することです。
package main
import (
"errors"
"fmt"
)
func SafeDivide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("ゼロで除算できません")
}
return a / b, nil
}
func main() {
result, err := SafeDivide(10, 0)
if err != nil {
fmt.Println("エラー:", err)
return
}
fmt.Println("結果:", result)
}
こうすればバグを事前に防げて、安全なコードになります。
4. panic/recoverで予期せぬエラーに備える
Goには意図的に深刻なエラーを起こさせてプログラムを停止し、その後回復させる仕組みがあります。ライブラリ内部やフレームワークで使われます。
package main
import "fmt"
func risky() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recover:", r)
}
}()
panic("何か重大な問題が発生しました")
}
func main() {
risky()
fmt.Println("ここまでプログラムが進んだ!")
}
recover: 何か重大な問題が発生しました
ここまでプログラムが進んだ!
この技法は万能ではないので、使いすぎに注意しましょう。
5. IDEやDelveを使ったステップ実行
最初はfmt.Printlnだけで十分ですが、本格的にデバッグするにはステップ実行が便利です。Visual Studio Codeなどの統合開発環境(IDE)や、コマンドラインデバッガーのDelve(dlv)を使えば、ブレークポイントで一時停止して変数を細かく調査できます。
6. よくあるバグと対処まとめ
- ゼロ除算:入力値を
if b == 0でチェック。 - nil参照:ポインタやマップ、スライスにアクセスする前に、必ず
nilチェックする。 - 型ミスマッチ:関数の引数や戻り値の型を確認。IDEで警告が出ることも。
- 文字列の誤字:キーや定数は間違えやすいので、定数化でミスを防止。
- 並行処理の競合:Goルーチンやチャネル使う場合はデータ競合に注意し、
go run -raceで検出。
7. デバッグを習慣にするために
バグは起こるものとして、ログ出力・入力チェック・ステップ実行などを当たり前の習慣にすると、トラブル対応が早くなります。仕組みが整っていくと、開発効率も上がります。
まとめ
はじめてGo言語で関数を作ったとき、うまく動かない瞬間はだれにでもあります。バグが出たり、意図しない値が返ってきたり、ゼロ除算でプログラムが止まったり、nil参照で原因がつかめなかったり、思ったより奥が深い世界です。しかし、この記事で学んだように、デバッグという作業は決して特別な技ではなく、すぐに試せる基本の積み重ねで身につきます。特に、ログ出力やfmt.Printlnで値を確認するやり方は、初心者でも一番取り入れやすい方法です。関数の途中で値を表示するだけで、引数が正しいのか、途中計算が想定どおりなのか、失敗する手前の状態はどうなっているのか、目に見えるようになるからです。
Go言語のログ出力はfmt.Printlnだけではなくlogパッケージでも扱えます。公式なログ管理ができ、時間やメッセージを残すことで、あとから読み返しやすくなります。プログラムが複雑になるほど、どこで失敗したのか記録が役立ち、デバッグが楽になります。日付やエラー内容がログに入っていれば、原因を探す作業も短くなり、再現しにくい不具合にも強くなります。関数の内部処理を読み返すとき、ただ結果を見るより、どういう流れで動いていたのかが自然に理解できるようになります。
さらに、バグを事前に防ぐ考え方もとても重要です。とくに多いのがゼロ除算の問題です。数式としては当然のように書けますが、bがゼロになる可能性を考えずにDivideや割り算を実行すると、パニックになりプログラムが止まります。初心者がつまずきやすいポイントですが、if文を使って条件を確認すれば回避できます。「入力値を検証する」ことは、安全な関数を書くための基本であり、バグ修正の手間を減らす考え方でもあります。nil参照、型の間違い、文字列の誤入力なども同じく、チェックをするだけで防げる場面が多くあります。
Go言語にはpanicとrecoverという仕組みがあり、深刻な問題が発生してもプログラム全体を止めずに後続の処理へ進めることができます。ただし、何があってもrecoverで止めればよい、という考え方は危険で、基本は入力チェックやログによる調査で原因をつかむことが大切です。panicは最後の手段であり、想定外の重大な問題が起きたときに役立つ道具です。使いどころを覚えると、より広いコードの世界を見ることができます。
もっと本格的に調べたいときは、IDEやDelveのようなデバッガーでステップ実行をする方法があります。ステップ実行はプログラムを一行ずつ動かして、変数の中身を細かく確認できます。ログではつかみにくい場面でも、ブレークポイントで止めて調査すれば、「なぜ想定と違う動きをしたのか」が理解しやすくなります。特に大きなプロジェクトでは、ステップ実行がデバッグの大きな力になります。Go言語は初心者にも扱いやすい環境が整っているので、じっくり学びながらステップアップできます。
実際には、これらの方法を組み合わせて使うことが多いです。ログで大まかな流れを見て、入力チェックで危ない値を防ぎ、ステップ実行で細かい原因を探すという流れです。関数のバグは必ずしも難しい原因ではなく、ほんの小さな計算ミスや、引数の渡し間違い、戻り値の確認不足などが中心です。慌てずに状況を整理すれば、自然と答えが見つかれます。Go言語はエラーメッセージも読みやすいので、落ち着いて文章を追えばどこが間違っているのか理解できます。
サンプルプログラムでイメージを深めよう
学んだ内容をまとめた、小さなサンプルです。
package main
import (
"errors"
"fmt"
"log"
)
func SafeAdd(a, b int) (int, error) {
log.Println("SafeAddに渡された値:", a, b)
if a < 0 || b < 0 {
return 0, errors.New("負の値は使えません")
}
return a + b, nil
}
func main() {
result, err := SafeAdd(2, 3)
if err != nil {
fmt.Println("エラー:", err)
return
}
fmt.Println("結果:", result)
}
関数の入り口でログを残し、入力チェックで危険な値を止め、最後に結果を確認するという流れです。このような考え方ができれば、関数のデバッグはぐっと楽になります。Go言語のデバッグは、特別な道具がなくても始められるので、練習しながら自然と慣れていけます。何度も試せば、どこを見るべきか、何を確認すればよいのか、体で覚えていくでしょう。関数の動きを理解できれば、他の処理でも役に立ちます。地道に続けるだけで、確かな実力になります。
生徒:きょうはGo言語のデバッグについて覚えました。バグが出ても、慌てず調べれば原因が見えるようになりそうです。
先生:そうですね。まずはログで状態を見えるようにして、入力チェックで危険な動きを防ぐことが大切です。
生徒:ゼロ除算やnil参照も、先に調べれば止められると知って安心しました。panicは最後の手段なんですね。
先生:その通りです。panicは「ここは絶対に失敗してはいけない」という場面で使うので、普段は落ち着いてデバッグするほうが効率的です。
生徒:ステップ実行も試してみます。変数がどう変わるのか見えると楽しそうです。
先生:小さな疑問でも、調査すると理解が深まります。続ければ、関数の動きを自分で追えるようになりますよ。
生徒:がんばって学習を続けます。ありがとうございます!
この記事を読んだ人からの質問
プログラミング初心者からのよくある疑問/質問を解決します
Go言語のデバッグでは、fmt.Printlnとlogのどちらを使えばいいですか?
初心者はまずfmt.Printlnで値を表示し、関数に渡された引数や戻り値を確認する方法がおすすめです。慣れてきたらlogパッケージを使うと、時刻付きのログやメッセージ管理ができ、Go言語の関数デバッグがより効率的になります。小さなプログラムならfmt.Println、大規模な開発や運用ではlogパッケージが便利です。
【超入門】ゼロから始めるGo言語プログラミング:最速で「動くアプリ」を作るマンツーマン指導
「プログラミングの仕組み」が根本からわかる。Go言語でバックエンド開発の第一歩を。
本講座を受講することで、単なる文法の暗記ではなく、「プログラムがコンピュータの中でどう動いているか」という本質的な理解につながります。シンプルながら強力なGo言語(Golang)を通じて、現代のバックエンドエンジニアに求められる基礎体力を最短距離で身につけます。
具体的な開発内容と環境
【つくるもの】
ターミナル(黒い画面)上で動作する「対話型計算プログラム」や、データを整理して表示する「ミニ・ツール」をゼロから作成します。自分の書いたコードが形になる感動を体験してください。
【開発環境】
プロの現場でシェアNo.1のVisual Studio Code (VS Code)を使用します。インストールから日本語化、Go言語用の拡張機能設定まで、現場基準の環境を一緒に構築します。
この60分で得られる3つの理解
「なぜ動くのか」という設定の仕組みを理解し、今後の独学で詰まらない土台を作ります。
データの種類やメモリの概念など、他言語にも通じるプログラミングの本質を学びます。
ただ動くだけでなく、誰が見ても分かりやすい「綺麗なコード」を書くための考え方を伝授します。
※本講座は、将来的にバックエンドエンジニアやクラウドインフラに興味がある未経験者のためのエントリー講座です。マンツーマン形式により、あなたの理解度に合わせて進行します。
初めてのGo言語を一緒に学びましょう!