[インデックス 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言語のエラーハンドリングのベストプラクティスを確立し、標準ライブラリ全体で一貫性のあるエラー処理メカニズムを導入するという目的があったと考えられます。具体的には、以下のような点が挙げられます。
- 標準
error
インターフェースへの統一: Go言語では、エラーはerror
という組み込みインターフェース(type error interface { Error() string }
)によって表現されます。このインターフェースに準拠することで、あらゆるエラーを統一的に扱うことが可能になります。このコミット以前は、os
パッケージやsyscall
パッケージで独自のError
インターフェースやNewError
関数が使われており、これがGo言語全体のエラー処理の統一性を損ねていました。 errors
パッケージの活用: Go 1.0のリリースに向けて、標準ライブラリとしてerrors
パッケージが導入されました。このパッケージは、シンプルなエラー文字列を生成するためのerrors.New
関数を提供します。このコミットは、カスタムのエラー生成メカニズムをerrors.New
に置き換えることで、より簡潔で標準的なエラー生成方法を採用しています。- コードの簡素化と保守性の向上: 独自の
Error
インターフェースやNewError
関数を維持することは、コードベースの複雑性を増し、将来的な変更や保守を困難にする可能性があります。標準的なerror
インターフェースとerrors
パッケージに移行することで、コードが簡素化され、Go言語の進化に合わせて容易に更新できるようになります。 - Windows固有の考慮事項: 変更箇所が
_windows.go
ファイルに集中していることから、Windows環境におけるシステムコールや環境変数に関するエラー処理の改善も意図されていた可能性があります。Windows APIはエラーコードを返すことが多く、それをGoのエラーモデルに適切にマッピングするための調整が必要でした。
これらの背景から、このコミットはGo言語のエラーハンドリングの設計思想をより深く反映させ、標準ライブラリの品質と一貫性を向上させるための重要なステップであったと言えます。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念とパッケージに関する知識が必要です。
1. Go言語のエラーハンドリング
Go言語は、例外処理(try-catchなど)のメカニズムを持たず、エラーを通常の戻り値として扱います。関数は通常、result, err
のように2つの値を返し、err
がnil
でない場合にエラーが発生したことを示します。
-
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のエラーハンドリングシステムに統合されます。DLL
とProc
: Windowsのダイナミックリンクライブラリ(DLL)と、そのDLL内のプロシージャ(関数)を表す型です。これらをロードしたり、プロシージャを検索したりする際にエラーが発生する可能性があります。
4. utf16
パッケージとunsafe
パッケージ
utf16
: UTF-16エンコーディングの文字列を扱うためのパッケージです。Windows APIは通常、UTF-16エンコードされた文字列を期待するため、GoのUTF-8文字列を変換する際に使用されます。unsafe
: Goの型安全性をバイパスする操作を可能にするパッケージです。ポインタ操作などで使用されますが、非常に注意して使用する必要があります。このコミットでは、主に文字列ポインタの変換などで使われています。
これらの前提知識を理解することで、コミットで行われた変更がGo言語のエラーハンドリングの哲学とどのように整合しているか、そしてWindows固有のシステムプログラミングの文脈でどのような意味を持つかが明確になります。
技術的詳細
このコミットの技術的な詳細は、主に以下の3つの変更点に集約されます。
-
カスタムエラーインターフェース
syscall.Error
の削除とerror
インターフェースへの統一:- 変更前:
syscall
パッケージには、Error
という独自のインターフェースが定義されていました。
このインターフェースは、Goの組み込み// An Error can represent any printable error condition. type Error interface { String() string }
error
インターフェースと似ていますが、メソッド名がString()
である点が異なります。 - 変更後: この
syscall.Error
インターフェースは完全に削除されました。これにより、syscall
パッケージ内のエラーはすべてGoの組み込みerror
インターフェース(Error() string
メソッドを持つ)に統一されます。
- 変更前:
-
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
メソッドを要求するためです。
- 変更前:
-
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.Wait
、Process.Signal
)でも、NewError
の代わりにerrors.New
が使われるようになりました。
- 変更前:
-
関数シグネチャの変更:
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"
が追加されました。
- Goの標準
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のコードベース全体でエラーがより統一的に扱われるようになり、開発者にとっての理解と利用が容易になります。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/f1b64aa7586551e0d433188a000481c29bc37c2e
- Go Code Review (CL): https://golang.org/cl/5333052
参考にした情報源リンク
- 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固有の実装において、エラーの取り扱い方を標準化することを目的としています。主な変更点は以下の通りです。
os
パッケージにおけるエラー生成の変更:os.NewError
関数の使用を停止し、代わりにGo標準のerrors
パッケージが提供するerrors.New
関数を使用するように変更されました。- これにより、
os.ENOENV
のような特定の環境変数エラーや、プロセス管理におけるエラーの生成方法が統一されました。
syscall
パッケージにおけるカスタムエラーインターフェースの廃止:syscall
パッケージ内に独自に定義されていたError
インターフェース(String() string
メソッドを持つ)が削除されました。syscall.Errno
型が、Goの組み込みerror
インターフェース(Error() string
メソッドを持つ)を満たすように、String()
メソッドがError()
メソッドにリネームされました。
- 関数シグネチャの変更:
syscall
パッケージ内のLoadDLL
、FindProc
、LazyDLL.Load
、LazyProc.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コードベースでは、標準ライブラリ内でもエラーの生成や表現方法に一貫性がない部分が存在していました。特に、os
やsyscall
のような低レベルのパッケージでは、独自のカスタムエラー型やエラー生成関数が使われていることがありました。
このコミットの背景には、以下のような目的があったと考えられます。
- Go言語のエラーハンドリング哲学の徹底: Goのエラーハンドリングの核となる「エラーは値である」という原則を、標準ライブラリの隅々まで浸透させること。
- 標準
error
インターフェースへの統一: 独自のカスタムエラーインターフェースや型を廃止し、Goの組み込みerror
インターフェースに完全に準拠させることで、ライブラリ間の相互運用性を高め、開発者がエラーをより統一的に扱えるようにすること。 errors
パッケージの活用: シンプルなエラーメッセージを生成する際には、errors.New
のような標準的な関数を使用することで、コードの簡潔性と可読性を向上させること。- コードベースの一貫性と保守性の向上: エラー処理のパターンを標準化することで、コードベース全体の理解を容易にし、将来的な変更やメンテナンスの負担を軽減すること。
- 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のエラーハンドリングシステムに統合されます。DLL
とProc
: Windowsのダイナミックリンクライブラリ(DLL)と、そのDLL内のプロシージャ(関数)を表す型です。これらをロードしたり、プロシージャを検索したりする際にエラーが発生する可能性があります。
4. utf16
パッケージとunsafe
パッケージ
utf16
: UTF-16エンコーディングの文字列を扱うためのパッケージです。Windows APIは通常、UTF-16エンコードされた文字列を期待するため、GoのUTF-8文字列を変換する際に使用されます。unsafe
: Goの型安全性をバイパスする操作を可能にするパッケージです。ポインタ操作などで使用されますが、非常に注意して使用する必要があります。このコミットでは、主に文字列ポインタの変換などで使われています。
これらの前提知識を理解することで、コミットで行われた変更がGo言語のエラーハンドリングの哲学とどのように整合しているか、そしてWindows固有のシステムプログラミングの文脈でどのような意味を持つかが明確になります。
技術的詳細
このコミットの技術的な変更は、Go言語のエラーハンドリングモデルへの深い理解と、Windowsシステムプログラミングの知識に基づいています。主要な技術的変更点は以下の通りです。
-
カスタムエラーインターフェース
syscall.Error
の削除:- 変更前は、
syscall
パッケージ内にtype Error interface { String() string }
という独自のインターフェースが存在していました。これはGoの組み込みerror
インターフェース(Error() string
)と似ていますが、メソッド名が異なっていました。 - このカスタムインターフェースを削除することで、
syscall
パッケージ内のエラー型がすべてGoの組み込みerror
インターフェースに統一され、Goエコシステム全体でのエラー処理の一貫性が確保されました。
- 変更前は、
-
syscall.Errno
型のString()
からError()
へのメソッド名変更:syscall.Errno
はWindowsのエラーコードをGoの型として表現するためのものです。変更前はfunc (e Errno) String() string
というメソッドを持っていました。- このメソッド名を
func (e Errno) Error() string
に変更することで、Errno
型がGoの組み込みerror
インターフェースを直接実装するようになりました。これにより、Windowsのシステムコールから返されるエラーコードも、Goの標準的なエラーとして透過的に扱えるようになります。
-
os.NewError
関数の廃止とerrors.New
への置き換え:os
パッケージでは、以前はNewError
という独自の関数を使用してエラーオブジェクトを生成していました。この関数は内部的にosError
という構造体をラップしていました。- このコミットでは、
os.NewError
関数が削除され、代わりにGoの標準ライブラリerrors
パッケージのerrors.New
関数が使用されるようになりました。errors.New
は、シンプルな文字列からerror
インターフェースを満たすエラーオブジェクトを生成する標準的な方法です。 - この変更により、
os.ENOENV
やos.Process
関連のエラー生成が、Goの標準的なエラー生成パターンに準拠するようになりました。これは、Goのコードベース全体でエラーの生成方法を統一し、開発者が新しいエラー型を学ぶ必要なく、既存の知識でエラーを扱えるようにするための重要なステップです。
-
関数シグネチャにおけるエラー型の統一:
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
関数を利用するための準備です。
- Goの標準
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のコードベース全体でエラーがより統一的に扱われるようになり、開発者にとっての理解と利用が容易になります。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/f1b64aa7586551e0d433188a000481c29bc37c2e
- Go Code Review (CL): https://golang.org/cl/5333052
参考にした情報源リンク
- 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.