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

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

このコミットは、Go言語の初期のtimeパッケージにおけるUTC()およびLocalTime()関数のシグネチャ変更に関するものです。具体的には、これらの関数がエラーを返す設計から、エラーを返さない設計へと修正されています。これは、Go言語のエラーハンドリング哲学、特に「発生し得ないエラー」に対するアプローチを反映した変更と考えられます。

コミット

  • コミットハッシュ: f58567d6e2a7df9c4d6145a70cd536c9fbf44d79
  • 作者: Russ Cox rsc@golang.org
  • コミット日時: 2008年12月9日 火曜日 10:27:30 -0800

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

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

元コミット内容

    more impossible time errors
    
    R=r
    DELTA=4  (0 added, 0 deleted, 4 changed)
    OCL=20788
    CL=20818

変更の背景

このコミットの背景には、Go言語のエラーハンドリングに関する設計思想があります。Go言語では、エラーは通常の戻り値として扱われ、if err != nilのような明示的なチェックが推奨されます。しかし、Goの設計哲学では、「発生し得ないエラー」に対しては、関数がエラーを返す必要はないとされています。

UTC()およびLocalTime()関数は、それぞれ協定世界時(UTC)とローカルタイムゾーンにおける現在時刻を*Time型で返すことを目的としています。これらの関数が時刻の取得に失敗するような、予期せぬ、あるいは「ありえない」状況が発生しない限り、エラーを返すシグネチャは冗長であり、Goのエラーハンドリングの原則に反すると判断された可能性があります。

コミットメッセージの「more impossible time errors」は、以前にも同様の「発生し得ないエラー」に関する修正が行われたことを示唆しており、Go言語の設計者がエラーの扱いについて一貫したアプローチを追求していたことが伺えます。これにより、APIの利用者は、これらの関数が常に有効な*Time値を返すことを信頼でき、不必要なエラーチェックのコードを記述する必要がなくなります。

前提知識の解説

Go言語のエラーハンドリング哲学

Go言語のエラーハンドリングは、他の多くの言語が採用する例外処理(try-catchなど)とは一線を画します。Goでは、エラーはerrorインターフェースを実装する通常の値として扱われます。

  • エラーは値である: 関数は、結果とエラーの2つの値を返すことが一般的です(例: value, err := someFunction())。呼び出し元は、if err != nilというパターンでエラーの有無を明示的にチェックし、適切に処理します。
  • 明示的なエラーハンドリング: このアプローチにより、開発者はエラーが発生しうるすべての箇所で、そのエラーをどのように扱うかを強制的に考慮することになります。これにより、エラーパスがコード内で明確になり、予期せぬ動作を防ぎます。
  • 例外の非採用: Goは意図的に例外メカニズムを採用していません。これは、例外がコードの制御フローを不明瞭にし、デバッグを困難にする可能性があるという思想に基づいています。
  • パニックとリカバリー: Goにはpanicrecoverというメカニズムも存在しますが、これらは回復不能なプログラミングエラー(例: nilポインタ参照)や、プログラムが安全に続行できないような非常に稀な状況のために予約されています。通常の予期されるエラー処理には使用されません。

この哲学に基づき、もしある操作が「絶対に失敗しない」と保証できる場合、その関数はエラーを返す必要がないと判断されます。

Go言語のtimeパッケージ

timeパッケージは、Go言語で時間と日付を扱うための機能を提供します。

  • time.Time: 特定の時点を表す構造体です。
  • time.Now(): 現在のローカル時刻をtime.Time型で返します。
  • time.UTC(): time.TimeオブジェクトをUTCに変換します。
  • time.Local(): time.Timeオブジェクトをシステムのローカルタイムゾーンに変換します。
  • time.LoadLocation(): 特定のタイムゾーンをロードするために使用されます。

このコミットで変更されたUTC()およびLocalTime()関数は、timeパッケージの初期バージョンに存在した関数で、それぞれ現在時刻をUTCまたはローカルタイムゾーンで取得する役割を担っていました。

技術的詳細

このコミットの技術的な変更は、src/lib/time/time.goファイル内のUTC()およびLocalTime()関数の関数シグネチャからエラー戻り値(err *os.Error)を削除した点に集約されます。

変更前:

export func UTC() (t *Time, err *os.Error) {
	return SecondsToUTC(Seconds()), nil
}

export func LocalTime() (t *Time, err *os.Error) {
	return SecondsToLocalTime(Seconds()), nil
}

変更後:

export func UTC() *Time {
	return SecondsToUTC(Seconds())
}

export func LocalTime() *Time {
	return SecondsToLocalTime(Seconds())
}

この変更は、これらの関数が内部的にSeconds()関数を呼び出し、その結果をSecondsToUTC()またはSecondsToLocalTime()に渡していることに基づいています。Seconds()関数は、1970年1月1日からの秒数を計算するものであり、システム時刻の取得が失敗するような状況は、Go言語のランタイムレベルで処理されるべきであり、これらの高レベルな関数でos.Errorとして捕捉する必要はない、という判断がなされたと考えられます。

つまり、これらの関数が時刻を取得する際に、アプリケーションレベルで回復可能なエラーが発生する可能性が極めて低い、あるいは存在しないと判断されたため、エラーを返すシグネチャは「不可能(impossible)」なエラーを表現していると見なされ、削除されました。これにより、関数の呼び出し側はエラーチェックのコードを省略でき、コードの簡潔性と可読性が向上します。

Go言語の設計では、エラーは予期される問題(例: ファイルが見つからない、ネットワーク接続が切れた)のために使用され、プログラムの論理的な欠陥や回復不能なシステムエラーはパニックとして扱われる傾向があります。このコミットは、UTC()LocalTime()のような基本的な時刻取得操作が、後者のカテゴリに属するようなエラーを直接返す必要はないというGoの設計原則を強化しています。

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

--- a/src/lib/time/time.go
+++ b/src/lib/time/time.go
@@ -145,8 +145,8 @@ export func SecondsToUTC(sec int64) *Time {
 	return t;
 }
 
-export func UTC() (t *Time, err *os.Error) {
-	return SecondsToUTC(Seconds()), nil
+export func UTC() *Time {
+	return SecondsToUTC(Seconds())
 }
 
 // TODO: Should this return an error?
@@ -161,8 +161,8 @@ export func SecondsToLocalTime(sec int64) *Time {
 	return t
 }
 
-export func LocalTime() (t *Time, err *os.Error) {
-	return SecondsToLocalTime(Seconds()), nil
+export func LocalTime() *Time {
+	return SecondsToLocalTime(Seconds())
 }
 
 // Compute number of seconds since January 1, 1970.

コアとなるコードの解説

上記の差分は、src/lib/time/time.goファイルにおけるUTC()LocalTime()関数の変更を示しています。

  1. UTC()関数の変更:

    • 変更前: export func UTC() (t *Time, err *os.Error)
      • このシグネチャは、*Time型の時刻オブジェクトと、*os.Error型のエラーオブジェクトの2つの戻り値を宣言していました。
      • return SecondsToUTC(Seconds()), nilという行は、常にnil(エラーなし)を返していました。
    • 変更後: export func UTC() *Time
      • 戻り値からerr *os.Errorが削除され、*Time型のみを返すようになりました。
      • return SecondsToUTC(Seconds())という行になり、nilを返す部分が不要になりました。
  2. LocalTime()関数の変更:

    • 変更前: export func LocalTime() (t *Time, err *os.Error)
      • UTC()関数と同様に、*Time型と*os.Error型の2つの戻り値を宣言していました。
      • return SecondsToLocalTime(Seconds()), nilという行は、常にnilを返していました。
    • 変更後: export func LocalTime() *Time
      • 戻り値からerr *os.Errorが削除され、*Time型のみを返すようになりました。
      • return SecondsToLocalTime(Seconds())という行になり、nilを返す部分が不要になりました。

この変更は、これらの関数が実際にエラーを返すことがない、つまり「不可能(impossible)」なエラーを宣言していたため、Go言語のエラーハンドリングの原則に従って、その冗長な部分を削除したものです。これにより、これらの関数を呼び出す側のコードはよりシンプルになり、不必要なエラーチェックのロジックを記述する必要がなくなります。これは、Go言語がコードの簡潔さと明示性を重視する設計思想の一例と言えます。

関連リンク

参考にした情報源リンク