Go言語入門:言語仕様 -Vol.145-

スポンサーリンク
Go言語入門:言語仕様 -Vol.145- 用語解説
Go言語入門:言語仕様 -Vol.145-
この記事は約5分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

本日は、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.Newfmt.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)
}

*DivideErrorError() string を持つので、自動的に error インターフェースを満たし、error を返す場所で返せるようになります。エラーに追加情報(ここでは割られる数)を持たせたいときに、こうした独自エラー型が役立ちます。


補足:この節はエラーの「型としての定義」だけを述べた、ごく短い導入です。実務では、エラーに文脈情報を付け足してラップする fmt.Errorf("...: %w", err) や、エラーの中身を調べる errors.Is / errors.As といった仕組みが頻繁に使われますが、それらは仕様書本体ではなく標準ライブラリの領域です。まずは「エラーは Error() string を持つインターフェースで、戻り値として返し、nil かどうかで判定する」という3点を押さえれば十分です。

おわりに 

本日は、Go言語の言語仕様について解説しました。

よっしー
よっしー

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

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました