Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 10376] ファイルの概要

このコミットは、2011年11月14日にAlex Brainmanによって行われた、Windows版のGoにおけるエラーハンドリングの修正です。doc/progs/file_windows.goファイルにおいて、新しいエラーハンドリング仕様に適合するために、従来の手動エラー変換コードを削除してコードの簡素化を行いました。

コミット

コミットハッシュ: 36494b0acdb3d2123f85f5506d7d3153ca66937d
作成者: Alex Brainman alex.brainman@gmail.com
日付: 2011年11月14日 20:53:03 +1100
メッセージ: doc/progs: fix windows version to satisfy new error
レビュー: R=rsc
CC: golang-dev
Code Review URL: https://golang.org/cl/5376089

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/36494b0acdb3d2123f85f5506d7d3153ca66937d

元コミット内容

このコミットは、doc/progs/file_windows.goファイルに対して以下の変更を行いました:

  • 1ファイル変更: doc/progs/file_windows.go
  • 5行追加、17行削除: 合計22行の変更でコードの大幅な簡素化を実現
  • 影響を受けた関数: OpenFile、Close、Read、Write

変更内容は、syscallからのエラー値処理を簡素化し、手動でのos.Errno変換コードを削除することでした。

変更の背景

2011年は、Go言語がバージョン1.0のリリースに向けて大きな変更を行っていた重要な時期でした。この時期には、エラーハンドリングシステムの標準化と改善が積極的に進められていました。

エラーハンドリングの歴史的変遷

Goのエラーハンドリングは以下のような発展を遂げました:

  1. 初期(2009年4月以前): *os.Error構造体を使用
  2. 2009年4月: os.Errorインターフェースに変更
  3. 2011年11月: このコミットの時期、Go 1に向けたエラーハンドリングの統一化
  4. Go 1.0(2012年3月): 現在のerrorインターフェースの確立

このコミットは、Go 1.0に向けた準備段階で行われた重要な標準化作業の一部です。特に、syscallパッケージから返されるエラーが自動的に適切なerror型に変換されるようになったため、手動での変換処理が不要になりました。

前提知識の解説

Alex Brainmanの貢献

Alex Brainmanは、GoのWindows移植において極めて重要な役割を果たした開発者です:

  • Go 1.0リリース以前からWindowsポートの責任者
  • Windows特有のsyscall実装の主要な貢献者
  • 長年にわたってGoのWindows サポートを事実上単独で維持
  • Windows APIとsyscallの専門知識を持つ

syscallパッケージとWindows

syscallパッケージは、オペレーティングシステムの低レベルプリミティブへのインターフェースを提供します:

  • 目的: OS固有のシステムコールへの統一されたアクセス
  • Windows特有の課題: Unix系OSとは異なるエラーコード体系
  • エラーハンドリング: Windows APIのエラーコードをGoのerror型に適切に変換

os.Errnoの役割

従来のコードでは、syscallから返される数値エラーコード(errno)を手動でos.Errno型に変換する必要がありました:

// 変更前のパターン
r, e := syscall.Open(name, mode, perm)
if e != 0 {
    err = os.Errno(e)
}

この手動変換は、エラーハンドリングの一貫性を保つために必要でしたが、コードの冗長性を生んでいました。

技術的詳細

エラーハンドリングの改善

このコミットの核心は、syscallパッケージの内部改善により、システムコールが直接適切なerror型を返すようになったことです。これにより以下の利点が生まれました:

  1. コードの簡素化: 手動エラー変換コードの削除
  2. 一貫性の向上: 統一されたエラーハンドリングパターン
  3. バグの減少: 手動変換に伴うミスの防止
  4. 保守性の向上: より読みやすく理解しやすいコード

型システムの改善

変更により、以下の型システムの改善が実現されました:

  • 直接的なerror型の使用: 中間的な型変換の排除
  • 型安全性の向上: コンパイル時のエラーチェック強化
  • Goイディオムの遵守: より自然なGoらしいエラーハンドリング

パフォーマンスへの影響

手動変換処理の削除により、わずかながらパフォーマンスの改善も期待できます:

  • 関数呼び出しの削減: os.Errno()呼び出しの削除
  • メモリ割り当ての最適化: 不要な中間オブジェクトの生成回避

コアとなるコードの変更箇所

OpenFile関数の変更

変更前:

func OpenFile(name string, mode int, perm uint32) (file *File, err error) {
    r, e := syscall.Open(name, mode, perm)
    if e != 0 {
        err = os.Errno(e)
    }
    return newFile(r, name), err
}

変更後:

func OpenFile(name string, mode int, perm uint32) (file *File, err error) {
    r, err := syscall.Open(name, mode, perm)
    return newFile(r, name), err
}

Close関数の変更

変更前:

func (file *File) Close() error {
    if file == nil {
        return os.EINVAL
    }
    e := syscall.Close(file.fd)
    file.fd = syscall.InvalidHandle // so it can't be closed again
    if e != 0 {
        return os.Errno(e)
    }
    return nil
}

変更後:

func (file *File) Close() error {
    if file == nil {
        return os.EINVAL
    }
    err := syscall.Close(file.fd)
    file.fd = syscall.InvalidHandle // so it can't be closed again
    return err
}

Read関数の変更

変更前:

func (file *File) Read(b []byte) (ret int, err error) {
    if file == nil {
        return -1, os.EINVAL
    }
    r, e := syscall.Read(file.fd, b)
    if e != 0 {
        err = os.Errno(e)
    }
    return int(r), err
}

変更後:

func (file *File) Read(b []byte) (ret int, err error) {
    if file == nil {
        return -1, os.EINVAL
    }
    r, err := syscall.Read(file.fd, b)
    return int(r), err
}

Write関数の変更

変更前:

func (file *File) Write(b []byte) (ret int, err error) {
    if file == nil {
        return -1, os.EINVAL
    }
    r, e := syscall.Write(file.fd, b)
    if e != 0 {
        err = os.Errno(e)
    }
    return int(r), err
}

変更後:

func (file *File) Write(b []byte) (ret int, err error) {
    if file == nil {
        return -1, os.EINVAL
    }
    r, err := syscall.Write(file.fd, b)
    return int(r), err
}

コアとなるコードの解説

共通パターンの改善

すべての変更に共通する改善パターンは以下の通りです:

  1. 変数名の変更: エラー値を格納する変数名をeからerrに変更し、Goの慣例に従った
  2. 条件分岐の削除: if e != 0チェックと手動変換処理を削除
  3. 直接返却: syscallから受け取ったerrorをそのまま返却

なぜこの変更が可能になったのか

この簡素化が可能になった理由:

  1. syscallパッケージの内部改善: syscall関数が適切なerror型を直接返すようになった
  2. エラー型の統一: Windows特有のエラーコードも統一されたerror型で表現可能になった
  3. 型システムの改善: Go 1.0に向けた型システムの洗練

セマンティクスの保持

重要なのは、この変更がAPIの動作を変更せず、内部実装を簡素化しただけであることです:

  • 外部インターフェースの維持: 関数シグネチャは変更なし
  • エラー情報の保持: 同じエラー情報が適切に伝播
  • 下位互換性: 既存のコードに影響なし

Goイディオムの採用

変更後のコードは、以下のGoイディオムをより適切に実装しています:

  1. エラーハンドリング: "errors are values"の哲学の体現
  2. 簡潔性: 不要な複雑さの排除
  3. 一貫性: 統一されたパターンの使用

関連リンク

参考にした情報源リンク