本文へスキップ
BecomeCoder

Goコース · 第8章 実務のGo · レッスン30

エラーと後始末 ― error・defer・panic

ブラウザで完結

導入

「うまくいかないかもしれない処理」をどう扱うかは、どの言語でも重要なテーマです。Go には例外(try/catch)がなく、代わりに error を戻り値として返す スタイルを徹底します。ここでは、これまで見てきた errordefer に加え、「本当に致命的な異常」のための panic を実務の視点でまとめます。

説明

まずは、エラー値と defer を組み合わせた典型的な形です。

package main

import (
	"errors"
	"fmt"
)

func withdraw(balance, amount int) (int, error) {
	if amount > balance {
		return balance, errors.New("残高が不足しています")
	}
	return balance - amount, nil
}

func main() {
	defer fmt.Println("取引処理を終了しました")

	newBalance, err := withdraw(1000, 1500)
	if err != nil {
		fmt.Println("エラー:", err)
		return
	}
	fmt.Println("新しい残高:", newBalance)
}
  • withdraw は「結果」と「エラー」の2つを返し、呼び出し側は if err != nil で必ず確認します。
  • defer fmt.Println(...) は、main の途中で return しても必ず実行されます。「エラーで早期リターンしても後始末は忘れない」という安心感が defer の価値です。

error は「予期される失敗」(残高不足・入力ミスなど)への対応です。一方 panic は、「プログラムを続けられないほど致命的な異常」が起きたときに使います。

func mustPositive(n int) int {
	if n < 0 {
		panic("負の数は扱えません") // ここで処理を強制的に打ち切る
	}
	return n
}

panic が起きると、その時点で関数の実行を中断し、呼び出し元へと巻き戻りながら defer だけは実行して、最終的にプログラムが異常終了します。

開始
後始末
panic: 想定外のエラー

上のように、panic が起きても defer で予約した後始末は実行されてから終了する――この順番を覚えておきましょう。

実際の Go には、defer の中で recover() を呼ぶことで panic を捕まえ、プログラムを止めずに処理を続ける仕組みもあります(if r := recover(); r != nil { ... } という形で書きます)。Web サーバーが1つのリクエスト処理で panic しても、サーバー全体を落とさずに済むのはこの recover のおかげです。ただし、このサイトの学習用シミュレータは recover の実際の動作には対応していないため、実際に試すには手元の Go 環境Go Playground を使ってください。

使い分けの目安

状況使うもの
入力ミス・残高不足など、起こりうる失敗error を返す
後始末(クローズ処理など)を必ず行いたいdefer
バグに近い、続行不可能な異常panic(呼び出し側のライブラリではまず使わない)

実務では、error を返せる場面で安易に panic を使わないのが原則です。panic はプログラム全体を止めてしまうため、ライブラリのコードでは特に慎重に使います。

やってみよう

withdraw(1000, 1500) の第2引数を 1000 以下の値に変えると、正常な取引として 新しい残高: ... が表示されます。defer で予約した「取引処理を終了しました」は、成功・失敗どちらの場合でも必ず表示される点を確かめてください。

演習

withdraw(1000, 1500) の呼び出しを withdraw(1000, 400) に変更して実行し、正常に引き出せた場合の残高と、defer によるメッセージの両方が表示されることを確認してください。

ヒント1を見る

newBalance, err := withdraw(1000, 400) のように、呼び出しの第2引数だけを書き換えます。

ヒント2を見る

amount(400)balance(1000) を超えないので errnil になり、fmt.Println("新しい残高:", newBalance) が実行されたあと、defer の行が最後に表示されます。

実際に動かしてみよう

下のエディタにGoを書いて「実行」を押すと、学習用シミュレータが fmt の出力を表示します(本物のGoコンパイラではなく、教材の範囲を再現した軽量エンジンです)。スライス・マップ・構造体・インターフェース・goroutine/channel なども動きます。本文の例を書き換えて動かしてみましょう(並行処理の実行順は決定的に再現しているため、本物のインターリーブとは異なる場合があります)。

Go — ブラウザ内で実行(学習用シミュレータ)

Goの教材サブセットを動かす学習用シミュレータを読み込みます(本物のGoコンパイラではなく、動きを再現した軽量な自作エンジンです)。
スクロールして表示された時点でも自動で読み込まれます。