[インデックス 10366] ファイルの概要
コミット
コミットハッシュ: fd34e78b53322114cfbcfa0af886a5a82a2f9ae5
作成者: Russ Cox rsc@golang.org
作成日: 2011年11月13日 22:42:42 -0500
コミットメッセージ: various: reduce overuse of os.EINVAL + others
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e50479ca889a319ffbb669236e949035a59fd82d
元コミット内容
このコミットでは、Go言語の標準パッケージ全体で汎用的なエラー値(特にos.EINVAL
)の過度な使用を減らし、より具体的で意味のあるエラーメッセージに置き換える大規模なリファクタリングが行われました。
主な変更箇所(15ファイル、36行追加、42行削除)
- bufio/bufio_test.go:
os.EPIPE
→io.ErrClosedPipe
- compress/lzw/reader.go:
os.EINVAL
→errClosed
(新規定義) - compress/lzw/writer.go:
os.EINVAL
→errClosed
(新規定義) - compress/lzw/writer_test.go:
os.EPIPE
エラーハンドリングを削除 - compress/zlib/writer_test.go:
os.EPIPE
エラーハンドリングを削除 - crypto/rand/util.go:
os.EINVAL
→errors.New("crypto/rand: prime size must be positive")
- crypto/tls/conn.go:
os.EAGAIN
の言及をnet.Error
に変更 - encoding/xml/xml_test.go:
os.EINVAL
→panic("unreachable")
- image/tiff/buffer.go:
os.EINVAL
→io.ErrUnexpectedEOF
- log/syslog/syslog.go:
os.EINVAL
→errors.New("log/syslog: invalid priority")
- mime/multipart/formdata.go:
os.EINVAL
→io.ErrUnexpectedEOF
- net/http/httputil/persist.go:
os.EBADF
→errClosed
(新規定義) - net/http/transport.go:
os.EINVAL
の使用を削除 - text/tabwriter/tabwriter.go:
os.EIO
→io.ErrShortWrite
- websocket/websocket.go:
os.EINVAL
→errSetTimeout
(新規定義)
変更の背景
2011年のGo言語開発初期段階において、多くのパッケージで汎用的なOS由来のエラー値(os.EINVAL
、os.EPIPE
、os.EBADF
など)が過度に使用されていました。これらのエラーは本来UNIXシステムコールの結果を表すものであり、Go言語の高レベルな抽象化には適していませんでした。
Russ Coxによるこの変更は、Goのエラー処理哲学を明確にし、より具体的で意味のあるエラーメッセージを提供することを目的としています。
前提知識の解説
UNIX errno とは
UNIX系システムでは、システムコールやライブラリ関数がエラーを返す際に、グローバル変数errno
に数値エラーコードを設定します。代表的なエラーコード:
EINVAL (22)
: 無効な引数EPIPE (32)
: パイプの破綻EBADF (9)
: 不正なファイル記述子EIO (5)
: I/OエラーEAGAIN (11)
: リソースが一時的に利用不可
Go言語のエラー処理哲学
Go言語では2011年の設計段階から、以下の原則に基づいてエラー処理を行っています:
- エラーは値である:
error
インターフェースを使用 - 明示的エラー処理: 例外ではなく戻り値でエラーを返す
- 文脈的エラーメッセージ: 何が起きたかを明確に説明
- コンテキストの追加: エラーをラップして情報を追加
技術的詳細
1. エラー値の分類と置換戦略
汎用エラーから具体的エラーへの変換パターン
パターン1: 新しい専用エラー変数の定義
// 変更前
func (d *decoder) Close() error {
d.err = os.EINVAL // 汎用的すぎる
return nil
}
// 変更後
var errClosed = errors.New("compress/lzw: reader/writer is closed")
func (d *decoder) Close() error {
d.err = errClosed // 具体的で意味のある
return nil
}
パターン2: 標準パッケージの適切なエラー値の使用
// 変更前
if int(off) >= len(r) || off < 0 {
return 0, os.EINVAL
}
// 変更後
if int(off) >= len(r) || off < 0 {
return 0, io.ErrUnexpectedEOF
}
パターン3: 即座に生成される説明的エラーメッセージ
// 変更前
if bits < 1 {
err = os.EINVAL
}
// 変更後
if bits < 1 {
err = errors.New("crypto/rand: prime size must be positive")
}
2. パッケージ別の詳細分析
compress/lzw パッケージ
LZW圧縮アルゴリズムの実装において、リーダー・ライター両方でerrClosed
エラーを統一的に定義。これにより、クローズされたリーダー/ライターに対する操作を明確に識別可能。
crypto/rand パッケージ
暗号学的乱数生成において、「素数サイズは正の値でなければならない」という具体的な制約を明示的に表現。
net/http パッケージ
HTTP通信での接続状態管理において、errClosed
を定義して接続がクローズされた状態を明確に表現。
コアとなるコードの変更箇所
1. compress/lzw/reader.go(53-74行目)
// 変更前
func (d *decoder) Close() error {
d.err = os.EINVAL // in case any Reads come along
return nil
}
// 変更後
var errClosed = errors.New("compress/lzw: reader/writer is closed")
func (d *decoder) Close() error {
d.err = errClosed // in case any Reads come along
return nil
}
2. crypto/rand/util.go(161-164行目)
// 変更前
func Prime(rand io.Reader, bits int) (p *big.Int, err error) {
if bits < 1 {
err = os.EINVAL
}
// ...
}
// 変更後
func Prime(rand io.Reader, bits int) (p *big.Int, err error) {
if bits < 1 {
err = errors.New("crypto/rand: prime size must be positive")
}
// ...
}
3. websocket/websocket.go(369-396行目)
// 変更前
func (ws *Conn) SetTimeout(nsec int64) error {
if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetTimeout(nsec)
}
return os.EINVAL
}
// 変更後
var errSetTimeout = errors.New("websocket: cannot set timeout: not using a net.Conn")
func (ws *Conn) SetTimeout(nsec int64) error {
if conn, ok := ws.rwc.(net.Conn); ok {
return conn.SetTimeout(nsec)
}
return errSetTimeout
}
コアとなるコードの解説
エラー処理の哲学的変化
この変更の核心は、「何が起きたかを正確に伝える」 というGo言語のエラー処理哲学の実現です。
1. 意味的明確性の向上
os.EINVAL
(「無効な引数」)→errors.New("crypto/rand: prime size must be positive")
(「素数サイズは正の値でなければならない」)- 開発者が具体的に何を修正すべきかを理解できる
2. 抽象化レベルの統一
- OS固有のエラーコードではなく、Go言語の抽象化レベルに適したエラー表現を使用
- プラットフォーム間の一貫性を保証
3. デバッグ効率の向上
- エラーメッセージから問題の発生箇所と原因を特定しやすい
- ログ解析やトラブルシューティングの効率化
パフォーマンスへの影響
この変更によるパフォーマンスへの影響は軽微です:
利点
- より具体的なエラーメッセージによるデバッグ時間の短縮
- エラーハンドリングコードの可読性向上
潜在的コスト
- 新しいエラー値の定義によるわずかなメモリ使用量の増加
- 文字列比較の場合のわずかな処理時間の増加
後方互換性の考慮
この変更は破壊的変更ではありませんが、エラー値の種類が変更されるため、エラー値の同等性チェックを行うコードに影響を与える可能性があります。