【ITニュース解説】Go (Golang) Basic - Error
2025年09月28日に「Dev.to」が公開したITニュース「Go (Golang) Basic - Error」について初心者にもわかりやすく解説しています。
ITニュース概要
Go言語ではエラーを特別なイベントではなく、関数が返す「値」として扱う。開発者は`if err != nil`でエラーを明示的にチェックし、予期される問題に意識的に対処することで、堅牢なコードを書く。`panic`は致命的なバグなど、予期せぬ事態でのみ使用する。
ITニュース解説
システムエンジニアとしてプログラムを開発する際、コードが常に完璧に動作するとは限らない。ファイルが見つからなかったり、ネットワーク接続が切れたり、ユーザーが不正な値を入力したりと、現実の世界ではさまざまな問題が発生する。これらの「うまくいかない状況」にどう対処するかは、堅牢なソフトウェアを作る上で極めて重要な要素である。多くのプログラミング言語は、このような状況を「例外(Exception)」として扱い、「try-catch」のような仕組みで処理するが、Go言語(Golang)は根本的に異なる、より明示的なアプローチを採用している。
Go言語において、エラーは「値」として扱われる。エラーはプログラムの実行を突然停止させる特別な出来事ではなく、文字列や数値と同じように、関数が返すことのできる単なる一つの値なのだ。この哲学は、開発者に対し、起こりうる失敗に対して意識的に対処するよう促し、その結果として、より予測可能で頑丈なコードを生み出すことに繋がる。
Go言語の核となる部分に、「error」という組み込みのインターフェースが存在する。これは非常にシンプルな「契約」であり、「error」として扱われるあらゆる型は、引数を持たず文字列を返す「Error()」という一つのメソッドを備えていなければならないと定められている。
1type error interface { 2 Error() string 3}
通常、開発者がゼロから独自のerror型を定義する必要はあまりないが、このインターフェースの仕組みを理解することで、Goのエラーシステムがいかに柔軟に設計されているかを知ることができる。
エラーが単なる値であるため、Go言語でエラーを処理する標準的な方法は、関数からエラーが返されたかどうかを確認することである。これにより、Goのコードベースで最も頻繁に目にするパターンの一つ、「if err != nil」が生まれる。失敗する可能性のある関数は通常、二つの値を返すように設計されている。一つは処理の結果、もう一つはエラー情報である。例えば、ある作業の報告書に例えるなら、作業が成功していれば報告書は白紙(nil)であり、何も問題がなかったことを意味する。もし何らかの問題が発生していれば、報告書には問題の詳細が記載されている(nilではない)。
このパターンを具体的なコードの動作で見てみよう。
1// ある処理を実行する関数の概念的な例 2result, err := someFunctionThatCanFail() 3if err != nil { 4 // エラーが発生した場合、このブロックが実行される 5 // ここでエラーメッセージを表示したり、適切な対処を行う 6 fmt.Println("エラーが発生しました:", err) 7 return // 処理を中断するなど 8} 9 10// ここにコードが到達した場合、'err'はnilであったことを意味する。 11// つまり、エラーは発生しておらず、'result'は安全に利用できる。 12fmt.Println("成功しました!結果は:", result)
このコードでは、someFunctionThatCanFail()という関数を呼び出し、その戻り値をresultとerrという二つの変数に代入している。その後、if err != nilという条件式で、errがnil(Goにおける「何もない」または「エラーなし」を意味する)ではないかどうかをチェックする。もしerrがnilでなければ、エラーが発生したと判断し、エラー処理のブロックが実行される。もしerrがnilであれば、エラーは発生していないと判断し、その後の処理(この場合はresultの利用)が安全に続行される。
では、開発者自身がエラーを作成し、関数から返すにはどうすればよいだろうか。Goの組み込みパッケージであるerrorsを使うと、これは非常に簡単に行える。実践的な例として、ゼロによる除算を試みた場合にエラーを返す「安全な除算関数」を作成してみよう。
1package main 2 3import ( 4 "errors" 5 "fmt" 6) 7 8// この関数は、float64型の結果とerror型のエラーの二つの値を返す 9func divide(a float64, b float64) (float64, error) { 10 // 割る数がゼロの場合、これは予期される失敗である 11 if b == 0 { 12 // ゼロ値(この場合は0.0)と新しいエラーメッセージを返す 13 return 0, errors.New("cannot divide by zero") 14 } 15 16 // すべて問題なければ、結果とエラーがなかったことを示す'nil'を返す 17 return a / b, nil 18} 19 20func main() { 21 // --- 成功するケース --- 22 result, err := divide(10.0, 2.0) 23 if err != nil { 24 // 'err'がnilなので、このブロックはスキップされる 25 fmt.Println("エラー:", err) 26 } else { 27 fmt.Println("結果 1:", result) 28 } 29 30 // --- 失敗するケース --- 31 result2, err2 := divide(10.0, 0) 32 if err2 != nil { 33 // エラーが返されたため、このブロックが実行される 34 fmt.Println("エラー:", err2) 35 } else { 36 fmt.Println("結果 2:", result2) 37 } 38}
divide関数では、bが0であるかをチェックし、もし0であれば、errors.New("cannot divide by zero")を使って新しいエラーを作成し、0という数値結果とともに返している。errors.Newは、引数として渡された文字列を内容とする新しいerror型の値を生成する。もしbが0でなければ、計算結果とnilを返し、エラーが発生しなかったことを呼び出し元に伝えている。このように、成功時には「結果, nil」を返し、失敗時には「ゼロ値, エラー」を返すパターンは、クリーンで堅牢なGoコードを書く上で不可欠な基礎である。
初心者にとってしばしば疑問となるのが、エラーを返す場合と「panic」を使用する場合の使い分けである。この二つの違いは、問題の「期待度」にある。
errorは、プログラムの動作の一部として予期される種類の問題を示す。例えば、ユーザーが無効な値を入力したり、ファイルが見つからなかったり、ネットワーク接続がタイムアウトしたりするような状況である。これらは、プログラムが認識し、適切に対処することで、多くの場合、実行を継続できる。
一方、panicは、予期されない、壊滅的な失敗を示す。これは、プログラマのバグや、到達してはならない状態にプログラムが陥った場合など、何かが根本的に間違っていることを意味する。panicが発生すると、プログラムは即座に実行を停止する。
この区別は非常に重要であり、以下のように使い分けるべきである。プログラムの通常の操作で発生する可能性のある、予期される失敗には**errorを使用する。panic**は非常に稀にしか使用せず、プログラマの明らかな誤りや、決して到達してはならないような、本当に例外的な状況のみに限定する。
Go言語のエラーハンドリングへのアプローチは、その最も特徴的な機能の一つである。エラーを通常の「値」として扱うことで、開発者は回復力があり、予測可能なアプリケーションを構築するように促される。この学習を通じて、Goにおける「エラーは値である」という哲学、エラーをチェックするための標準的な「if err != nil」パターン、独自のエラーを作成して返す方法、そして管理可能なエラーと致命的なpanicとの重要な違いについて理解を深めたことだろう。