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

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

このコミットは、Go言語の標準ライブラリであるosパッケージとsyscallパッケージにおけるエラーハンドリングのメカニズムを更新するものです。具体的には、カスタムのエラー型やNewError関数を廃止し、Go言語の標準的なerrorインターフェースとerrorsパッケージを利用するように変更しています。これにより、エラー処理の一貫性とGo言語のエラーハンドリングの慣習への準拠が強化されています。

コミット

  • コミットハッシュ: f1b64aa7586551e0d433188a000481c29bc37c2e
  • Author: Russ Cox rsc@golang.org
  • Date: Tue Nov 1 22:12:41 2011 -0400
  • コミットメッセージ:
    os, syscall: update for error
    
    R=adg
    CC=golang-dev
    https://golang.org/cl/5333052
    

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

https://github.com/golang/go/commit/f1b64aa7586551e0d433188a000481c29bc37c2e

元コミット内容

このコミットは、osパッケージとsyscallパッケージにおけるエラー処理を、Go言語の標準的なerrorインターフェースに準拠させるための変更です。具体的には、osパッケージ内のNewError関数やsyscallパッケージ内のErrorインターフェースの定義を削除し、代わりにerrors.New関数や組み込みのerrorインターフェースを使用するようにコードを修正しています。これにより、エラーの生成と返却がよりGo言語の慣習に沿った形になります。

変更の背景

この変更が行われた2011年頃は、Go言語がまだ比較的新しく、標準ライブラリの設計が活発に行われていた時期です。Go言語のエラーハンドリングは、例外処理ではなく多値戻り値(value, error)とerrorインターフェースを使用するという独特のアプローチを採用しています。初期のGo言語のコードベースでは、エラー処理に関して様々な試行錯誤が行われていました。

このコミットの背景には、Go言語のエラーハンドリングのベストプラクティスを確立し、標準ライブラリ全体で一貫性のあるエラー処理メカニズムを導入するという目的があったと考えられます。具体的には、以下のような点が挙げられます。

  1. 標準errorインターフェースへの統一: Go言語では、エラーはerrorという組み込みインターフェース(type error interface { Error() string })によって表現されます。このインターフェースに準拠することで、あらゆるエラーを統一的に扱うことが可能になります。このコミット以前は、osパッケージやsyscallパッケージで独自のErrorインターフェースやNewError関数が使われており、これがGo言語全体のエラー処理の統一性を損ねていました。
  2. errorsパッケージの活用: Go 1.0のリリースに向けて、標準ライブラリとしてerrorsパッケージが導入されました。このパッケージは、シンプルなエラー文字列を生成するためのerrors.New関数を提供します。このコミットは、カスタムのエラー生成メカニズムをerrors.Newに置き換えることで、より簡潔で標準的なエラー生成方法を採用しています。
  3. コードの簡素化と保守性の向上: 独自のErrorインターフェースやNewError関数を維持することは、コードベースの複雑性を増し、将来的な変更や保守を困難にする可能性があります。標準的なerrorインターフェースとerrorsパッケージに移行することで、コードが簡素化され、Go言語の進化に合わせて容易に更新できるようになります。
  4. Windows固有の考慮事項: 変更箇所が_windows.goファイルに集中していることから、Windows環境におけるシステムコールや環境変数に関するエラー処理の改善も意図されていた可能性があります。Windows APIはエラーコードを返すことが多く、それをGoのエラーモデルに適切にマッピングするための調整が必要でした。

これらの背景から、このコミットはGo言語のエラーハンドリングの設計思想をより深く反映させ、標準ライブラリの品質と一貫性を向上させるための重要なステップであったと言えます。

前提知識の解説

このコミットを理解するためには、以下のGo言語の概念とパッケージに関する知識が必要です。

1. Go言語のエラーハンドリング

Go言語は、例外処理(try-catchなど)のメカニズムを持たず、エラーを通常の戻り値として扱います。関数は通常、result, errのように2つの値を返し、errnilでない場合にエラーが発生したことを示します。

  • errorインターフェース: Go言語におけるエラーは、すべて組み込みのerrorインターフェースを満たす型です。このインターフェースは、Error() stringという単一のメソッドを定義しています。

    type error interface {
        Error() string
    }
    

    これにより、どのような型でもError() stringメソッドを実装していれば、error型として扱うことができます。

  • errorsパッケージ: Goの標準ライブラリerrorsパッケージは、エラーを生成するための基本的な機能を提供します。

    • errors.New(text string): 指定された文字列をエラーメッセージとする新しいエラーを生成します。これは、特定の状況で発生する一般的なエラーを示すのに使われます。

2. osパッケージ

osパッケージは、オペレーティングシステム(OS)の機能にアクセスするためのプラットフォームに依存しないインターフェースを提供します。ファイル操作、プロセス管理、環境変数へのアクセスなどが含まれます。

  • 環境変数: os.Getenvなどの関数を使って環境変数を読み取ることができます。環境変数が存在しない場合など、エラーが発生する可能性があります。
  • プロセス管理: os.Process型は実行中のプロセスを表し、Waitメソッドなどでプロセスの終了を待機したり、Signalメソッドでシグナルを送信したりできます。これらの操作中にエラーが発生することもあります。

3. syscallパッケージ

syscallパッケージは、低レベルのオペレーティングシステムプリミティブへのインターフェースを提供します。これは、OS固有のシステムコールを直接呼び出すために使用されます。通常、アプリケーション開発者が直接使用することは稀で、osパッケージのような高レベルのパッケージが内部的に利用します。

  • Windows API: Windows環境では、syscallパッケージはWindows API関数を呼び出すためのラッパーを提供します。DLLのロード(LoadDLL)やプロシージャのアドレス取得(FindProc)などが含まれます。
  • Errno: syscall.Errnoは、システムコールが返すエラーコード(Windowsのエラーコードなど)を表す型です。この型もerrorインターフェースを満たすことで、Goのエラーハンドリングシステムに統合されます。
  • DLLProc: Windowsのダイナミックリンクライブラリ(DLL)と、そのDLL内のプロシージャ(関数)を表す型です。これらをロードしたり、プロシージャを検索したりする際にエラーが発生する可能性があります。

4. utf16パッケージとunsafeパッケージ

  • utf16: UTF-16エンコーディングの文字列を扱うためのパッケージです。Windows APIは通常、UTF-16エンコードされた文字列を期待するため、GoのUTF-8文字列を変換する際に使用されます。
  • unsafe: Goの型安全性をバイパスする操作を可能にするパッケージです。ポインタ操作などで使用されますが、非常に注意して使用する必要があります。このコミットでは、主に文字列ポインタの変換などで使われています。

これらの前提知識を理解することで、コミットで行われた変更がGo言語のエラーハンドリングの哲学とどのように整合しているか、そしてWindows固有のシステムプログラミングの文脈でどのような意味を持つかが明確になります。

技術的詳細

このコミットの技術的な詳細は、主に以下の3つの変更点に集約されます。

  1. カスタムエラーインターフェースsyscall.Errorの削除とerrorインターフェースへの統一:

    • 変更前: syscallパッケージには、Errorという独自のインターフェースが定義されていました。
      // An Error can represent any printable error condition.
      type Error interface {
          String() string
      }
      
      このインターフェースは、Goの組み込みerrorインターフェースと似ていますが、メソッド名がString()である点が異なります。
    • 変更後: このsyscall.Errorインターフェースは完全に削除されました。これにより、syscallパッケージ内のエラーはすべてGoの組み込みerrorインターフェース(Error() stringメソッドを持つ)に統一されます。
  2. Errno型のString()メソッドからError()メソッドへの変更:

    • 変更前: syscall.Errno型は、String() stringメソッドを実装していました。
      func (e Errno) String() string { return Errstr(int(e)) }
      
    • 変更後: String()メソッドがError()メソッドにリネームされました。
      func (e Errno) Error() string { return Errstr(int(e)) }
      
      この変更により、Errno型がGoの組み込みerrorインターフェースを直接満たすようになります。これは、errorインターフェースがError() stringメソッドを要求するためです。
  3. os.NewError関数の廃止とerrors.Newへの置き換え:

    • 変更前: osパッケージには、NewErrorという関数が定義されており、これを使ってエラーオブジェクトを生成していました。
      // NewError returns an Error that describes the error.
      func NewError(s string) Error { return &osError{s} }
      
      そして、ENOENVのような特定のOSエラーはNewErrorを使って定義されていました。
      var ENOENV = NewError("no such environment variable")
      
    • 変更後: os.NewError関数は削除され、代わりにGoの標準ライブラリerrorsパッケージのerrors.New関数が使用されるようになりました。
      var ENOENV = errors.New("no such environment variable")
      
      また、osパッケージ内の他のエラー生成箇所(例: Process.WaitProcess.Signal)でも、NewErrorの代わりにerrors.Newが使われるようになりました。
  4. 関数シグネチャの変更:

    • syscallパッケージ内のLoadDLL, FindProc, LazyDLL.Load, LazyProc.Findといった関数やメソッドの戻り値のerrの型が、カスタムのsyscall.Errorから組み込みのerrorインターフェース型に変更されました。
      • 例: func LoadDLL(name string) (dll *DLL, err Error)func LoadDLL(name string) (dll *DLL, err error) に変更。

これらの変更は、Go言語のエラーハンドリングの設計原則である「エラーは値である」という考え方をより強く推進し、標準ライブラリ全体で一貫したエラー処理の慣習を確立することを目的としています。これにより、開発者はGoのエラーをより予測可能で統一的な方法で扱うことができるようになります。

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

diff --git a/src/pkg/os/env_windows.go b/src/pkg/os/env_windows.go
index 9fc61974c4..795da21a44 100644
--- a/src/pkg/os/env_windows.go
+++ b/src/pkg/os/env_windows.go
@@ -7,13 +7,14 @@
 package os
 
 import (
+"errors"
"syscall"
"utf16"
"unsafe"
)
 
 // ENOENV is the error indicating that an environment variable does not exist.
-var ENOENV = NewError("no such environment variable")
+var ENOENV = errors.New("no such environment variable")
 
 // Getenverror retrieves the value of the environment variable named by the key.
 // It returns the value and an error, if any.
diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go
index b2b640c871..866757e312 100644
--- a/src/pkg/os/exec_windows.go
+++ b/src/pkg/os/exec_windows.go
@@ -5,6 +5,7 @@
 package os
 
 import (
+"errors"
"runtime"
"syscall"
)
@@ -17,7 +18,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) {
 	case syscall.WAIT_FAILED:
 		return nil, NewSyscallError("WaitForSingleObject", e)
 	default:
-		return nil, NewError("os: unexpected result from WaitForSingleObject")
+		return nil, errors.New("os: unexpected result from WaitForSingleObject")
 	}\n 	var ec uint32
 	e = syscall.GetExitCodeProcess(syscall.Handle(p.handle), &ec)
@@ -31,7 +32,7 @@ func (p *Process) Signal(sig Signal) error {
 // Signal sends a signal to the Process.
 func (p *Process) Signal(sig Signal) error {
 	if p.done {
-		return NewError("os: process already finished")
+		return errors.New("os: process already finished")
 	}\n 	switch sig.(UnixSignal) {
 	case SIGKILL:
diff --git a/src/pkg/syscall/dll_windows.go b/src/pkg/syscall/dll_windows.go
index 1873d0c90d..6815dee058 100644
--- a/src/pkg/syscall/dll_windows.go
+++ b/src/pkg/syscall/dll_windows.go
@@ -8,15 +8,10 @@ import (
"sync"
)
 
-// An Error can represent any printable error condition.
-type Error interface {
-	String() string
-}
-
 // Errno is the Windows error number.
 type Errno uint64
 
-func (e Errno) String() string { return Errstr(int(e)) }\n+func (e Errno) Error() string { return Errstr(int(e)) }\n 
 // DLLError describes reasons for DLL load failures.
 type DLLError struct {
 	Msg string
@@ -42,7 +37,7 @@ type DLL struct {
 }\n 
 // LoadDLL loads DLL file into memory.
-func LoadDLL(name string) (dll *DLL, err Error) {
+func LoadDLL(name string) (dll *DLL, err error) {
 	h, e := loadlibrary(StringToUTF16Ptr(name))
 	if e != 0 {
 		return nil, &DLLError{
@@ -69,7 +64,7 @@ func MustLoadDLL(name string) *DLL {
 
 // FindProc searches DLL d for procedure named name and returns *Proc
 // if found. It returns an error if search fails.
-func (d *DLL) FindProc(name string) (proc *Proc, err Error) {
+func (d *DLL) FindProc(name string) (proc *Proc, err error) {
 	a, e := getprocaddress(uintptr(d.Handle), StringBytePtr(name))
 	if e != 0 {
 		return nil, &DLLError{
@@ -160,7 +155,7 @@ type LazyDLL struct {
 
 // Load loads DLL file d.Name into memory. It returns an error if fails.
 // Load will not try to load DLL, if it is already loaded into memory.
-func (d *LazyDLL) Load() Error {
+func (d *LazyDLL) Load() error {
 	if d.dll == nil {
 		d.mu.Lock()
 		defer d.mu.Unlock()
@@ -211,7 +206,7 @@ type LazyProc struct {
 // Find searches DLL for procedure named p.Name. It returns
 // an error if search fails. Find will not search procedure,
 // if it is already found and loaded into memory.
-func (p *LazyProc) Find() Error {
+func (p *LazyProc) Find() error {
 	if p.proc == nil {
 		p.mu.Lock()
 		defer p.mu.Unlock()

コアとなるコードの解説

このコミットは、Go言語のosおよびsyscallパッケージにおけるエラーハンドリングの標準化を目的としています。以下に、各ファイルの変更点を詳細に解説します。

src/pkg/os/env_windows.go

  • import "errors" の追加:
    • Goの標準errorsパッケージを使用するために、import文に"errors"が追加されました。
  • ENOENV の定義変更:
    • 変更前: var ENOENV = NewError("no such environment variable")
    • 変更後: var ENOENV = errors.New("no such environment variable")
    • osパッケージ内で独自に定義されていたNewError関数が廃止され、代わりにerrors.New関数を使ってエラーオブジェクトを生成するように変更されました。これにより、環境変数が存在しないことを示すエラーENOENVが、Goの標準的なエラー生成方法に準拠するようになりました。

src/pkg/os/exec_windows.go

  • import "errors" の追加:
    • env_windows.goと同様に、errorsパッケージを使用するためにimport文が追加されました。
  • Process.Wait メソッド内のエラー生成の変更:
    • 変更前: return nil, NewError("os: unexpected result from WaitForSingleObject")
    • 変更後: return nil, errors.New("os: unexpected result from WaitForSingleObject")
    • WaitForSingleObjectシステムコールから予期せぬ結果が返された場合のエラー生成も、NewErrorからerrors.Newに置き換えられました。
  • Process.Signal メソッド内のエラー生成の変更:
    • 変更前: return NewError("os: process already finished")
    • 変更後: return errors.New("os: process already finished")
    • 既に終了しているプロセスにシグナルを送信しようとした場合のエラーも、NewErrorからerrors.Newに置き換えられました。

src/pkg/syscall/dll_windows.go

このファイルは、WindowsのDLL(Dynamic Link Library)と関連するシステムコールを扱うためのGoのインターフェースを提供します。ここでの変更は、エラーハンドリングの標準化において最も重要な部分です。

  • カスタムエラーインターフェースErrorの削除:

    • 変更前には、以下のカスタムインターフェースが定義されていました。
      // An Error can represent any printable error condition.
      type Error interface {
          String() string
      }
      
    • このインターフェースは完全に削除されました。これにより、syscallパッケージ内のエラーは、Goの組み込みerrorインターフェース(Error() stringメソッドを持つ)に統一されます。
  • Errno型のString()メソッドからError()メソッドへの変更:

    • 変更前: func (e Errno) String() string { return Errstr(int(e)) }
    • 変更後: func (e Errno) Error() string { return Errstr(int(e)) }
    • syscall.Errno型はWindowsのエラーコードを表しますが、この変更により、Errno型がGoの組み込みerrorインターフェースを直接満たすようになりました。これは、errorインターフェースがError() stringメソッドを要求するためです。これにより、Errno型の値も他のGoのエラーと同様に扱うことができるようになります。
  • 関数シグネチャの変更:

    • LoadDLL関数:
      • 変更前: func LoadDLL(name string) (dll *DLL, err Error)
      • 変更後: func LoadDLL(name string) (dll *DLL, err error)
      • 戻り値のerrの型が、カスタムのsyscall.Errorから組み込みのerrorインターフェース型に変更されました。
    • FindProcメソッド:
      • 変更前: func (d *DLL) FindProc(name string) (proc *Proc, err Error)
      • 変更後: func (d *DLL) FindProc(name string) (proc *Proc, err error)
      • 同様に、DLL内のプロシージャを検索するFindProcメソッドのerrの型も変更されました。
    • LazyDLL.Loadメソッド:
      • 変更前: func (d *LazyDLL) Load() Error
      • 変更後: func (d *LazyDLL) Load() error
      • 遅延ロードされるDLLをロードするLoadメソッドの戻り値の型も変更されました。
    • LazyProc.Findメソッド:
      • 変更前: func (p *LazyProc) Find() Error
      • 変更後: func (p *LazyProc) Find() error
      • 遅延検索されるプロシージャを検索するFindメソッドの戻り値の型も変更されました。

これらの変更は、Go言語のエラーハンドリングのベストプラクティスに準拠し、標準ライブラリ全体でエラー処理の一貫性を高めるための重要なステップです。これにより、Goのコードベース全体でエラーがより統一的に扱われるようになり、開発者にとっての理解と利用が容易になります。

関連リンク

参考にした情報源リンク

  • Go言語公式ドキュメント: https://go.dev/
  • Go言語のエラーハンドリングに関する公式ブログ記事やドキュメント (当時の情報に基づく):
    • "Error handling and Go" (Go Blog): https://go.dev/blog/error-handling-and-go (このコミットより後の記事ですが、Goのエラーハンドリングの哲学を理解する上で参考になります)
  • Go言語のosパッケージドキュメント: https://pkg.go.dev/os
  • Go言語のsyscallパッケージドキュメント: https://pkg.go.dev/syscall
  • Go言語のerrorsパッケージドキュメント: https://pkg.go.dev/errors
  • Go言語の歴史と進化に関する情報 (当時のGo言語の状況を理解する上で参考になります)

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

このコミットは、Go言語の標準ライブラリであるosパッケージとsyscallパッケージにおけるエラーハンドリングのメカニズムを更新するものです。具体的には、これまで使用されていたカスタムのエラー型やエラー生成関数を廃止し、Go言語の標準的なerrorインターフェースとerrorsパッケージを利用するように変更しています。これにより、エラー処理の一貫性が向上し、Go言語のエラーハンドリングの慣習への準拠が強化されています。特に、Windows固有のシステムコールを扱う部分で、エラーの表現方法が標準化されています。

コミット

  • コミットハッシュ: f1b64aa7586551e0d433188a000481c29bc37c2e
  • Author: Russ Cox rsc@golang.org
  • Date: Tue Nov 1 22:12:41 2011 -0400
  • コミットメッセージ:
    os, syscall: update for error
    
    R=adg
    CC=golang-dev
    https://golang.org/cl/5333052
    

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

https://github.com/golang/go/commit/f1b64aa7586551e0d433188a000481c29bc37c2e

元コミット内容

このコミットは、Go言語のosパッケージとsyscallパッケージ、特にWindows固有の実装において、エラーの取り扱い方を標準化することを目的としています。主な変更点は以下の通りです。

  1. osパッケージにおけるエラー生成の変更:
    • os.NewError関数の使用を停止し、代わりにGo標準のerrorsパッケージが提供するerrors.New関数を使用するように変更されました。
    • これにより、os.ENOENVのような特定の環境変数エラーや、プロセス管理におけるエラーの生成方法が統一されました。
  2. syscallパッケージにおけるカスタムエラーインターフェースの廃止:
    • syscallパッケージ内に独自に定義されていたErrorインターフェース(String() stringメソッドを持つ)が削除されました。
    • syscall.Errno型が、Goの組み込みerrorインターフェース(Error() stringメソッドを持つ)を満たすように、String()メソッドがError()メソッドにリネームされました。
  3. 関数シグネチャの変更:
    • syscallパッケージ内のLoadDLLFindProcLazyDLL.LoadLazyProc.Findといった関数やメソッドの戻り値のエラー型が、カスタムのsyscall.ErrorからGoの組み込みerrorインターフェース型に変更されました。

これらの変更により、Go言語のエラーハンドリングの原則である「エラーは値である」という考え方がより徹底され、標準ライブラリ全体で一貫したエラー処理の慣習が確立されました。

変更の背景

このコミットが行われた2011年頃は、Go言語がまだ開発の初期段階にあり、Go 1.0のリリースに向けて標準ライブラリの設計と実装が活発に行われていた時期です。Go言語のエラーハンドリングは、例外処理ではなく、エラーを通常の戻り値として扱うという独特のアプローチを採用しています。この設計思想は、エラーフローを明示的にし、予期せぬ例外によるプログラムの中断を防ぐことを目的としています。

当時のGo言語のエラーハンドリングには、以下のような特徴がありました。

  • errorインターフェース: Goは、Error() stringメソッドを持つ組み込みのerrorインターフェースを提供し、エラーを表現するための統一的なメカニズムを提供していました。
  • errors.New: errorsパッケージは、シンプルな文字列からエラーを作成するためのerrors.New関数を提供していました。
  • 明示的なチェック: 関数呼び出し後にif err != nilと明示的にエラーをチェックすることがGoのイディオムでした。

しかし、初期のGoコードベースでは、標準ライブラリ内でもエラーの生成や表現方法に一貫性がない部分が存在していました。特に、ossyscallのような低レベルのパッケージでは、独自のカスタムエラー型やエラー生成関数が使われていることがありました。

このコミットの背景には、以下のような目的があったと考えられます。

  1. Go言語のエラーハンドリング哲学の徹底: Goのエラーハンドリングの核となる「エラーは値である」という原則を、標準ライブラリの隅々まで浸透させること。
  2. 標準errorインターフェースへの統一: 独自のカスタムエラーインターフェースや型を廃止し、Goの組み込みerrorインターフェースに完全に準拠させることで、ライブラリ間の相互運用性を高め、開発者がエラーをより統一的に扱えるようにすること。
  3. errorsパッケージの活用: シンプルなエラーメッセージを生成する際には、errors.Newのような標準的な関数を使用することで、コードの簡潔性と可読性を向上させること。
  4. コードベースの一貫性と保守性の向上: エラー処理のパターンを標準化することで、コードベース全体の理解を容易にし、将来的な変更やメンテナンスの負担を軽減すること。
  5. Windows固有の課題への対応: Windows環境では、システムコールが数値のエラーコードを返すことが一般的です。これらのエラーコードをGoのエラーモデルに適切にマッピングし、標準的なerrorインターフェースを通じて表現できるようにすることが重要でした。

このように、このコミットはGo言語のエラーハンドリングの設計を成熟させ、標準ライブラリの品質と一貫性を高めるための重要な一歩でした。

前提知識の解説

このコミットの変更内容を深く理解するためには、以下のGo言語の概念と関連パッケージに関する知識が不可欠です。

1. Go言語のエラーハンドリングの基本

Go言語は、エラーを例外として扱うのではなく、通常の戻り値として扱います。これは、関数が通常、結果とエラーの2つの値を返すことで実現されます(例: value, err := someFunction())。

  • errorインターフェース: Goにおけるエラーは、すべて組み込みのerrorインターフェースを満たす型です。このインターフェースは、Error() stringという単一のメソッドを定義しています。

    type error interface {
        Error() string
    }
    

    この設計により、どのような型でもError() stringメソッドを実装していれば、error型として扱うことができ、Goのエラーハンドリングシステムに統合されます。

  • errorsパッケージ: Goの標準ライブラリerrorsパッケージは、エラーを生成するための基本的な機能を提供します。

    • errors.New(text string): 指定された文字列をエラーメッセージとする新しいエラーを生成します。これは、特定の状況で発生する一般的なエラーを示すのに最もシンプルで一般的な方法です。

2. osパッケージ

osパッケージは、オペレーティングシステム(OS)の機能にアクセスするためのプラットフォームに依存しないインターフェースを提供します。ファイル操作、プロセス管理、環境変数へのアクセスなどが含まれます。

  • 環境変数: os.Getenvなどの関数を使って環境変数を読み取ることができます。環境変数が存在しない場合など、エラーが発生する可能性があります。
  • プロセス管理: os.Process型は実行中のプロセスを表し、Waitメソッドなどでプロセスの終了を待機したり、Signalメソッドでシグナルを送信したりできます。これらの操作中にエラーが発生することもあります。

3. syscallパッケージ

syscallパッケージは、低レベルのオペレーティングシステムプリミティブへのインターフェースを提供します。これは、OS固有のシステムコールを直接呼び出すために使用されます。通常、アプリケーション開発者が直接使用することは稀で、osパッケージのような高レベルのパッケージが内部的に利用します。

  • Windows API: Windows環境では、syscallパッケージはWindows API関数を呼び出すためのラッパーを提供します。DLLのロード(LoadDLL)やプロシージャのアドレス取得(FindProc)などが含まれます。
  • Errno: syscall.Errnoは、システムコールが返すエラーコード(Windowsのエラーコードなど)を表す型です。この型もerrorインターフェースを満たすことで、Goのエラーハンドリングシステムに統合されます。
  • DLLProc: Windowsのダイナミックリンクライブラリ(DLL)と、そのDLL内のプロシージャ(関数)を表す型です。これらをロードしたり、プロシージャを検索したりする際にエラーが発生する可能性があります。

4. utf16パッケージとunsafeパッケージ

  • utf16: UTF-16エンコーディングの文字列を扱うためのパッケージです。Windows APIは通常、UTF-16エンコードされた文字列を期待するため、GoのUTF-8文字列を変換する際に使用されます。
  • unsafe: Goの型安全性をバイパスする操作を可能にするパッケージです。ポインタ操作などで使用されますが、非常に注意して使用する必要があります。このコミットでは、主に文字列ポインタの変換などで使われています。

これらの前提知識を理解することで、コミットで行われた変更がGo言語のエラーハンドリングの哲学とどのように整合しているか、そしてWindows固有のシステムプログラミングの文脈でどのような意味を持つかが明確になります。

技術的詳細

このコミットの技術的な変更は、Go言語のエラーハンドリングモデルへの深い理解と、Windowsシステムプログラミングの知識に基づいています。主要な技術的変更点は以下の通りです。

  1. カスタムエラーインターフェースsyscall.Errorの削除:

    • 変更前は、syscallパッケージ内にtype Error interface { String() string }という独自のインターフェースが存在していました。これはGoの組み込みerrorインターフェース(Error() string)と似ていますが、メソッド名が異なっていました。
    • このカスタムインターフェースを削除することで、syscallパッケージ内のエラー型がすべてGoの組み込みerrorインターフェースに統一され、Goエコシステム全体でのエラー処理の一貫性が確保されました。
  2. syscall.Errno型のString()からError()へのメソッド名変更:

    • syscall.ErrnoはWindowsのエラーコードをGoの型として表現するためのものです。変更前はfunc (e Errno) String() stringというメソッドを持っていました。
    • このメソッド名をfunc (e Errno) Error() stringに変更することで、Errno型がGoの組み込みerrorインターフェースを直接実装するようになりました。これにより、Windowsのシステムコールから返されるエラーコードも、Goの標準的なエラーとして透過的に扱えるようになります。
  3. os.NewError関数の廃止とerrors.Newへの置き換え:

    • osパッケージでは、以前はNewErrorという独自の関数を使用してエラーオブジェクトを生成していました。この関数は内部的にosErrorという構造体をラップしていました。
    • このコミットでは、os.NewError関数が削除され、代わりにGoの標準ライブラリerrorsパッケージのerrors.New関数が使用されるようになりました。errors.Newは、シンプルな文字列からerrorインターフェースを満たすエラーオブジェクトを生成する標準的な方法です。
    • この変更により、os.ENOENVos.Process関連のエラー生成が、Goの標準的なエラー生成パターンに準拠するようになりました。これは、Goのコードベース全体でエラーの生成方法を統一し、開発者が新しいエラー型を学ぶ必要なく、既存の知識でエラーを扱えるようにするための重要なステップです。
  4. 関数シグネチャにおけるエラー型の統一:

    • syscallパッケージ内のDLLロードやプロシージャ検索に関連する関数(LoadDLL, FindProc, LazyDLL.Load, LazyProc.Find)の戻り値のエラー型が、カスタムのsyscall.ErrorからGoの組み込みerrorインターフェース型に変更されました。
    • 例えば、func LoadDLL(name string) (dll *DLL, err Error)func LoadDLL(name string) (dll *DLL, err error) に変更されました。
    • この変更は、関数が返すエラーが常にGoの標準errorインターフェースに準拠することを保証し、呼び出し側が型アサーションや型スイッチを使って特定のエラー型を処理する際に、より予測可能な動作を可能にします。

これらの技術的変更は、Go言語のエラーハンドリングの設計原則である「エラーは値である」という考え方をより強く推進し、標準ライブラリ全体で一貫したエラー処理の慣習を確立することを目的としています。これにより、開発者はGoのエラーをより予測可能で統一的な方法で扱うことができるようになります。

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

diff --git a/src/pkg/os/env_windows.go b/src/pkg/os/env_windows.go
index 9fc61974c4..795da21a44 100644
--- a/src/pkg/os/env_windows.go
+++ b/src/pkg/os/env_windows.go
@@ -7,13 +7,14 @@
 package os
 
 import (
+"errors"
"syscall"
"utf16"
"unsafe"
)
 
 // ENOENV is the error indicating that an environment variable does not exist.
-var ENOENV = NewError("no such environment variable")
+var ENOENV = errors.New("no such environment variable")
 
 // Getenverror retrieves the value of the environment variable named by the key.
 // It returns the value and an error, if any.
diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go
index b2b640c871..866757e312 100644
--- a/src/pkg/os/exec_windows.go
+++ b/src/pkg/os/exec_windows.go
@@ -5,6 +5,7 @@
 package os
 
 import (
+"errors"
"runtime"
"syscall"
)
@@ -17,7 +18,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) {
 	case syscall.WAIT_FAILED:
 		return nil, NewSyscallError("WaitForSingleObject", e)
 	default:
-		return nil, NewError("os: unexpected result from WaitForSingleObject")
+		return nil, errors.New("os: unexpected result from WaitForSingleObject")
 	}\n 	var ec uint32
 	e = syscall.GetExitCodeProcess(syscall.Handle(p.handle), &ec)
@@ -31,7 +32,7 @@ func (p *Process) Signal(sig Signal) error {
 // Signal sends a signal to the Process.
 func (p *Process) Signal(sig Signal) error {
 	if p.done {
-		return NewError("os: process already finished")
+		return errors.New("os: process already finished")
 	}\n 	switch sig.(UnixSignal) {
 	case SIGKILL:
diff --git a/src/pkg/syscall/dll_windows.go b/src/pkg/syscall/dll_windows.go
index 1873d0c90d..6815dee058 100644
--- a/src/pkg/syscall/dll_windows.go
+++ b/src/pkg/syscall/dll_windows.go
@@ -8,15 +8,10 @@ import (
"sync"
)
 
-// An Error can represent any printable error condition.
-type Error interface {
-	String() string
-}
-
 // Errno is the Windows error number.
 type Errno uint64
 
-func (e Errno) String() string { return Errstr(int(e)) }\n+func (e Errno) Error() string { return Errstr(int(e)) }\n 
 // DLLError describes reasons for DLL load failures.
 type DLLError struct {
 	Msg string
@@ -42,7 +37,7 @@ type DLL struct {
 }\n 
 // LoadDLL loads DLL file into memory.
-func LoadDLL(name string) (dll *DLL, err Error) {
+func LoadDLL(name string) (dll *DLL, err error) {
 	h, e := loadlibrary(StringToUTF16Ptr(name))
 	if e != 0 {
 		return nil, &DLLError{
@@ -69,7 +64,7 @@ func MustLoadDLL(name string) *DLL {
 
 // FindProc searches DLL d for procedure named name and returns *Proc
 // if found. It returns an error if search fails.
-func (d *DLL) FindProc(name string) (proc *Proc, err Error) {
+func (d *DLL) FindProc(name string) (proc *Proc, err error) {
 	a, e := getprocaddress(uintptr(d.Handle), StringBytePtr(name))
 	if e != 0 {
 		return nil, &DLLError{
@@ -160,7 +155,7 @@ type LazyDLL struct {
 
 // Load loads DLL file d.Name into memory. It returns an error if fails.
 // Load will not try to load DLL, if it is already loaded into memory.
-func (d *LazyDLL) Load() Error {
+func (d *LazyDLL) Load() error {
 	if d.dll == nil {
 		d.mu.Lock()
 		defer d.mu.Unlock()
@@ -211,7 +206,7 @@ type LazyProc struct {
 // Find searches DLL for procedure named p.Name. It returns
 // an error if search fails. Find will not search procedure,
 // if it is already found and loaded into memory.
-func (p *LazyProc) Find() Error {
+func (p *LazyProc) Find() error {
 	if p.proc == nil {
 		p.mu.Lock()
 		defer p.mu.Unlock()

コアとなるコードの解説

このコミットは、Go言語のosおよびsyscallパッケージにおけるエラーハンドリングの標準化を目的としています。以下に、各ファイルの変更点を詳細に解説します。

src/pkg/os/env_windows.go

このファイルは、Windows環境における環境変数関連の操作を扱います。

  • import "errors" の追加:
    • Goの標準errorsパッケージを使用するために、import文に"errors"が追加されました。これは、errors.New関数を利用するための準備です。
  • ENOENV の定義変更:
    • 変更前: var ENOENV = NewError("no such environment variable")
    • 変更後: var ENOENV = errors.New("no such environment variable")
    • osパッケージ内で独自に定義されていたNewError関数が廃止され、代わりにerrors.New関数を使ってエラーオブジェクトを生成するように変更されました。これにより、環境変数が存在しないことを示すエラーENOENVが、Goの標準的なエラー生成方法に準拠するようになりました。

src/pkg/os/exec_windows.go

このファイルは、Windows環境における外部プロセスの実行と管理を扱います。

  • import "errors" の追加:
    • env_windows.goと同様に、errorsパッケージを使用するためにimport文が追加されました。
  • Process.Wait メソッド内のエラー生成の変更:
    • 変更前: return nil, NewError("os: unexpected result from WaitForSingleObject")
    • 変更後: return nil, errors.New("os: unexpected result from WaitForSingleObject")
    • WaitForSingleObjectシステムコールから予期せぬ結果が返された場合のエラー生成も、NewErrorからerrors.Newに置き換えられました。
  • Process.Signal メソッド内のエラー生成の変更:
    • 変更前: return NewError("os: process already finished")
    • 変更後: return errors.New("os: process already finished")
    • 既に終了しているプロセスにシグナルを送信しようとした場合のエラーも、NewErrorからerrors.Newに置き換えられました。

src/pkg/syscall/dll_windows.go

このファイルは、WindowsのDLL(Dynamic Link Library)と関連するシステムコールを扱うためのGoのインターフェースを提供します。ここでの変更は、エラーハンドリングの標準化において最も重要な部分です。

  • カスタムエラーインターフェースErrorの削除:

    • 変更前には、以下のカスタムインターフェースが定義されていました。
      // An Error can represent any printable error condition.
      type Error interface {
          String() string
      }
      
    • このインターフェースは完全に削除されました。これにより、syscallパッケージ内のエラーは、Goの組み込みerrorインターフェース(Error() stringメソッドを持つ)に統一されます。
  • Errno型のString()メソッドからError()メソッドへの変更:

    • 変更前: func (e Errno) String() string { return Errstr(int(e)) }
    • 変更後: func (e Errno) Error() string { return Errstr(int(e)) }
    • syscall.Errno型はWindowsのエラーコードを表しますが、この変更により、Errno型がGoの組み込みerrorインターフェースを直接満たすようになりました。これは、errorインターフェースがError() stringメソッドを要求するためです。これにより、Errno型の値も他のGoのエラーと同様に扱うことができるようになります。
  • 関数シグネチャの変更:

    • LoadDLL関数:
      • 変更前: func LoadDLL(name string) (dll *DLL, err Error)
      • 変更後: func LoadDLL(name string) (dll *DLL, err error)
      • 戻り値のerrの型が、カスタムのsyscall.Errorから組み込みのerrorインターフェース型に変更されました。
    • FindProcメソッド:
      • 変更前: func (d *DLL) FindProc(name string) (proc *Proc, err Error)
      • 変更後: func (d *DLL) FindProc(name string) (proc *Proc, err error)
      • 同様に、DLL内のプロシージャを検索するFindProcメソッドのerrの型も変更されました。
    • LazyDLL.Loadメソッド:
      • 変更前: func (d *LazyDLL) Load() Error
      • 変更後: func (d *LazyDLL) Load() error
      • 遅延ロードされるDLLをロードするLoadメソッドの戻り値の型も変更されました。
    • LazyProc.Findメソッド:
      • 変更前: func (p *LazyProc) Find() Error
      • 変更後: func (p *LazyProc) Find() error
      • 遅延検索されるプロシージャを検索するFindメソッドの戻り値の型も変更されました。

これらの変更は、Go言語のエラーハンドリングのベストプラクティスに準拠し、標準ライブラリ全体でエラー処理の一貫性を高めるための重要なステップです。これにより、Goのコードベース全体でエラーがより統一的に扱われるようになり、開発者にとっての理解と利用が容易になります。

関連リンク

参考にした情報源リンク

  • Go言語公式ドキュメント: https://go.dev/
  • Go言語のエラーハンドリングに関する公式ブログ記事やドキュメント (当時の情報に基づく):
    • "Error handling and Go" (Go Blog): https://go.dev/blog/error-handling-and-go (このコミットより後の記事ですが、Goのエラーハンドリングの哲学を理解する上で参考になります)
  • Go言語のosパッケージドキュメント: https://pkg.go.dev/os
  • Go言語のsyscallパッケージドキュメント: https://pkg.go.dev/syscall
  • Go言語のerrorsパッケージドキュメント: https://pkg.go.dev/errors
  • Go言語のエラーハンドリングの進化に関するWeb検索結果:
    • In 2011, Go's error handling was characterized by its explicit approach, treating errors as values rather than exceptions, a design choice that distinguished it from many other languages like Java or Python.
    • Key aspects of Go's error handling in 2011 included: The error interface, errors.New, fmt.Errorf, explicit checking, verbosity, and no exceptions.
    • While the core principles of Go's error handling remained consistent, later versions introduced features like error wrapping (using fmt.Errorf with %w) to add context to errors without losing the original cause, addressing some of the verbosity and debugging challenges of earlier implementations. However, the fundamental concept of errors as values and explicit checking was already established in 2011.