
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
エラー
事前宣言された型 error は、次のように定義されています。
type error interface {
Error() string
}
これは、エラーの状態を表現するための慣例的なインターフェースであり、nil 値がエラーなしを表します。たとえば、ファイルからデータを読み取る関数は、次のように定義されるかもしれません。
func Read(f *File, b []byte) (n int, err error)
解説
結論:error はGoに最初から組み込まれているエラー表現用のインターフェース。Error() string メソッドさえ持てば何でもエラーとして扱える。エラーがないことは nil で表す。
error は組み込みのインターフェース
error は、Goに**最初から用意されている(事前宣言された、predeclared)**型です。自分で定義しなくても、どこでもそのまま使えます。
その正体はインターフェースで、たった1つのメソッドを要求するだけです。
type error interface {
Error() string
}
これは「Error() という、文字列を返すメソッドを持っているものは、すべて error として扱える」という意味です。インターフェースとは「持つべきメソッドの一覧」を定めた約束事で、その約束を満たせば中身が何であってもそのインターフェース型として通用します。
資格に例えると、「Error() というメソッドを実装している」ことが資格の取得条件で、それさえ満たせば「エラーを名乗る資格あり」と認められる、というイメージです。型の名前は問われず、条件を満たすかどうかだけが見られます。
nil が「エラーなし」を表す
エラーが発生していない正常な状態は、error の値を nil にすることで表します。前の節で学んだとおり、インターフェースのゼロ値は nil です。
これがGoのエラーハンドリングの基本パターンを生みます。
n, err := Read(f, b)
if err != nil { // err が nil でない = エラーが起きた
// エラー処理
return err
}
// err が nil = 正常。続きの処理へ
「err != nil なら異常、nil なら正常」という判定が、Goのコードのいたるところに登場します。信号機に例えると、nil が「青(問題なし、進め)」、nil 以外が「赤(問題発生、止まって対処せよ)」のような目印です。
エラーは「戻り値」として返す
原文の例が、Goのエラーの扱い方の典型を示しています。
func Read(f *File, b []byte) (n int, err error)
注目すべきは、error が戻り値の1つとして返される点です。多くの他言語では例外(exception)を投げて処理を中断させますが、Goは例外を多用せず、関数の戻り値としてエラーを返すスタイルを採用しています。
この Read 関数は2つの値を返します。n int(読み取れたバイト数)と err error(エラー情報)です。慣例として、エラーは戻り値の最後に置くのがGoの作法です。
呼び出す側は、こう書きます。
n, err := Read(f, b)
if err != nil {
// 読み取りに失敗したときの処理
}
// 成功時は n バイト読めている
例外方式が「問題が起きたら処理を投げ飛ばして中断する」のに対し、Goの方式は「問題の有無を戻り値で受け取り、その場で確認する」ものです。エラーが目に見える形で必ず返ってくるので、見落としにくく、処理の流れが追いやすいという利点があります。
自分でエラーを作るには
最も手軽なのは、標準ライブラリの errors.New や fmt.Errorf を使う方法です。
import "errors"
func Divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("0で割ることはできません") // エラーを作って返す
}
return a / b, nil // 正常時は nil を返す
}
error はインターフェースなので、Error() string メソッドを実装すれば独自のエラー型を作ることもできます。
type DivideError struct {
Dividend int
}
func (e *DivideError) Error() string { // この1メソッドで error になる
return fmt.Sprintf("%d を0で割ろうとしました", e.Dividend)
}
*DivideError は Error() string を持つので、自動的に error インターフェースを満たし、error を返す場所で返せるようになります。エラーに追加情報(ここでは割られる数)を持たせたいときに、こうした独自エラー型が役立ちます。
補足:この節はエラーの「型としての定義」だけを述べた、ごく短い導入です。実務では、エラーに文脈情報を付け足してラップする fmt.Errorf("...: %w", err) や、エラーの中身を調べる errors.Is / errors.As といった仕組みが頻繁に使われますが、それらは仕様書本体ではなく標準ライブラリの領域です。まずは「エラーは Error() string を持つインターフェースで、戻り値として返し、nil かどうかで判定する」という3点を押さえれば十分です。
おわりに
本日は、Go言語の言語仕様について解説しました。

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)

コメント