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

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

このコミットは、Go言語の標準ライブラリにおいて、os.Time関数の使用を廃止し、関連する依存関係の循環問題を解決するための変更です。具体的には、io/ioutilパッケージとnetパッケージからos.Timeへの依存を取り除き、timeパッケージ内でのio/ioutilの使用をbytes.Bufferを用いたファイル読み込みに置き換えることで、依存関係の循環を解消しています。

コミット

commit a6c501e4b1c0bc21e85a51ef62af65c4f284fff1
Author: Anthony Martin <ality@pbrane.org>
Date:   Fri Nov 11 14:40:41 2011 -0500

    net, io/ioutil: remove use of os.Time
    
    I had to replace the single use of io/ioutil
    in the time package with a bytes.Buffer since
    there would've been a dependency cycle.
    
    There are no other uses of os.Time.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5372054

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

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

元コミット内容

このコミットの元の内容は、os.Time関数の使用をnetパッケージとio/ioutilパッケージから削除することです。また、timeパッケージがio/ioutilを使用している唯一の箇所をbytes.Bufferに置き換えることで、timeパッケージとio/ioutil間の依存関係の循環を解消しています。コミットメッセージは、os.Timeの他の使用箇所がないことを明記しています。

変更の背景

この変更の背景には、Go言語の標準ライブラリ設計における依存関係の整理と、より適切なAPIの選択があります。

  1. os.Timeの廃止: os.Timeは、Goの初期バージョンに存在した関数で、システム時刻を秒とナノ秒で返すものでした。しかし、Goの設計思想として、特定の機能はより専門的なパッケージに集約されるべきという考え方があります。時刻に関する機能はtimeパッケージに集約されるべきであり、osパッケージが時刻情報を提供する役割を持つのは適切ではないと判断されたと考えられます。これにより、timeパッケージが提供するより高レベルで柔軟な時刻API(例: time.Now(), time.Nanoseconds(), time.Seconds())への移行が促されました。

  2. 依存関係の循環 (Dependency Cycle): コミットメッセージで言及されている「dependency cycle」は、Goのパッケージ管理において非常に重要な問題です。Goでは、パッケージAがパッケージBをインポートし、同時にパッケージBがパッケージAをインポートするような循環参照は許されません。 このコミットでは、timeパッケージがio/ioutilをインポートし、同時にio/ioutilosをインポートし、さらにostimeパッケージの機能(os.Time)に依存しているという状況が発生していた可能性があります。具体的には、timeパッケージがio/ioutil.ReadFileを使ってタイムゾーン情報を読み込み、一方でio/ioutilos.Timeを使って一時ファイル名を生成していたため、time -> io/ioutil -> os -> timeという循環参照が生じていたと推測されます。 このような循環参照は、コンパイルエラーを引き起こすか、あるいはコードの理解と保守を極めて困難にします。このコミットは、timeパッケージがio/ioutilに依存する部分を、より低レベルなos.Openbytes.Bufferを使った直接的なファイル読み込みに置き換えることで、この循環を断ち切っています。

  3. APIの洗練: os.Timeのような低レベルな時刻取得APIは、より抽象化されたtimeパッケージの関数に置き換えられることで、コードの可読性と堅牢性が向上します。time.Nanoseconds()time.Seconds()は、それぞれナノ秒単位、秒単位のUnixエポックからの経過時間を返すため、os.Timeが返していた秒とナノ秒のタプルを扱うよりもシンプルです。

前提知識の解説

このコミットを理解するためには、以下のGo言語の基本的な概念と標準ライブラリの知識が必要です。

  • Go言語のパッケージシステム: Goのコードはパッケージに分割され、importキーワードを使って他のパッケージの機能を利用します。Goのビルドシステムは、パッケージ間の依存関係を解決しますが、循環参照は許可されません。
  • osパッケージ: オペレーティングシステムと対話するための機能(ファイル操作、プロセス管理、環境変数など)を提供します。
  • timeパッケージ: 時刻と日付を扱うための機能(現在時刻の取得、時刻のフォーマット、時間間隔の計算など)を提供します。
  • io/ioutilパッケージ: I/O操作に関するユーティリティ関数を提供します。特にReadFileは、ファイルの内容をバイトスライスとして一度に読み込む便利な関数です。
  • netパッケージ: ネットワークI/Oに関する機能(TCP/UDP接続、DNSルックアップなど)を提供します。
  • bytesパッケージ: バイトスライスを操作するための機能を提供します。bytes.Bufferは、可変長のバイトバッファとして機能し、io.Readerio.Writerインターフェースを実装しているため、I/O操作の中間バッファとして非常に便利です。
  • os.Time() (旧API): Goの初期バージョンに存在した関数で、func Time() (sec, nsec int64, mono int64) のように、Unixエポックからの秒数とナノ秒数を返していました。このコミットの時点では既に非推奨または削除の対象となっていました。
  • time.Nanoseconds() / time.Seconds(): timeパッケージが提供する関数で、それぞれUnixエポックからの経過時間をナノ秒単位、秒単位でint64として返します。これらはos.Time()の代替として使用されます。
  • os.Open(): osパッケージの関数で、指定されたパスのファイルを読み取り用に開きます。ファイルディスクリプタ(*os.File)を返します。
  • io.Readerインターフェース: Read(p []byte) (n int, err error)メソッドを持つインターフェースで、データを読み込むことができる型が実装します。
  • bytes.Buffer.ReadFrom(r io.Reader): bytes.Bufferのメソッドで、io.ReaderからEOFに達するまでデータを読み込み、バッファに追加します。

技術的詳細

このコミットの技術的詳細は、Go言語のパッケージ設計原則と、特定のAPIの進化に焦点を当てています。

os.Timeの廃止と代替

os.Timeは、Goの初期段階でシステム時刻を取得するために使用されていましたが、その役割はより専門的なtimeパッケージに移行されました。os.Timeは、秒とナノ秒を別々に返す形式でしたが、timeパッケージのtime.Nanoseconds()time.Seconds()は、単一のint64値としてナノ秒または秒単位のUnixエポックからの経過時間を返します。これにより、時刻の扱いがより統一され、簡潔になりました。

  • io/ioutil/tempfile.goでの変更:

    • 変更前: sec, nsec, _ := os.Time() を使用して時刻を取得し、uint32(sec*1e9 + nsec + int64(os.Getpid())) で乱数のシードを生成していました。
    • 変更後: uint32(time.Nanoseconds() + int64(os.Getpid())) を使用しています。time.Nanoseconds()は直接ナノ秒単位のint64を返すため、秒とナノ秒を結合する手間が省かれ、コードが簡潔になっています。
  • net/hosts.goでの変更:

    • 変更前: now, _, _ := os.Time() を使用して現在時刻を取得し、hosts.time, _, _ = os.Time() でキャッシュの更新時刻を設定していました。
    • 変更後: now := time.Seconds()hosts.time = time.Seconds() を使用しています。ここでは、キャッシュの有効期限を秒単位で管理しているため、time.Seconds()が適切です。

依存関係の循環の解消

最も重要な変更は、src/pkg/time/zoneinfo_unix.goにおけるio/ioutil.ReadFileの使用の置き換えです。

  • 問題の構造:

    • timeパッケージはタイムゾーン情報をファイルから読み込むためにio/ioutil.ReadFileを使用。
    • io/ioutilパッケージは一時ファイル名を生成するためにos.Timeを使用。
    • os.Timeは、timeパッケージの内部実装に依存している(または、osパッケージがtimeパッケージをインポートしている)。
    • これにより、time -> io/ioutil -> os -> timeという循環参照が発生し、Goのビルドシステムがこれを解決できませんでした。
  • 解決策:

    • io/ioutil.ReadFile(name)の代わりに、より低レベルなファイル操作とbytes.Bufferを組み合わせることで、timeパッケージからio/ioutilへの直接的な依存を解消しました。
    • 具体的には、os.Open(name)でファイルを開き、そのファイルディスクリプタ(*os.File)をbytes.BufferReadFromメソッドに渡して内容を読み込んでいます。
    • bytes.Bufferio.Readerインターフェースを実装しているため、f.ReadFrom(f)のように*os.Fileから直接データを読み込むことができます。これにより、io/ioutilを介さずにファイルの内容をメモリにロードすることが可能になります。

この変更により、timeパッケージはosパッケージとbytesパッケージにのみ依存するようになり、io/ioutilへの依存がなくなりました。結果として、循環参照が解消され、パッケージ構造がより健全になりました。

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

src/pkg/io/ioutil/tempfile.go

--- a/src/pkg/io/ioutil/tempfile.go
+++ b/src/pkg/io/ioutil/tempfile.go
@@ -8,6 +8,7 @@ import (
  	"os"
  	"path/filepath"
  	"strconv"
+	"time"
 )
 
 // Random number state, accessed without lock; racy but harmless.
@@ -17,8 +18,7 @@ var rand uint32
 
 func reseed() uint32 {
-	sec, nsec, _ := os.Time()
-	return uint32(sec*1e9 + nsec + int64(os.Getpid()))
+	return uint32(time.Nanoseconds() + int64(os.Getpid()))
 }
 
 func nextSuffix() string {

src/pkg/net/hosts.go

--- a/src/pkg/net/hosts.go
+++ b/src/pkg/net/hosts.go
@@ -7,8 +7,8 @@
  package net
 
  import (
-	"os"
  	"sync"
+	"time"
  )
 
  const cacheMaxAge = int64(300) // 5 minutes.
@@ -26,7 +26,7 @@ var hosts struct {
  }
 
  func readHosts() {
-	now, _, _ := os.Time()
+	now := time.Seconds()
  	hp := hostsPath
  	if len(hosts.byName) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp {
  		hs := make(map[string][]string)
@@ -51,7 +51,7 @@ func readHosts() {
  			}
  		}
  		// Update the data cache.
-		hosts.time, _, _ = os.Time()
+		hosts.time = time.Seconds()
  		hosts.path = hp
  		hosts.byName = hs
  		hosts.byAddr = is

src/pkg/time/zoneinfo_unix.go

--- a/src/pkg/time/zoneinfo_unix.go
+++ b/src/pkg/time/zoneinfo_unix.go
@@ -12,7 +12,7 @@
  package time
 
  import (
-	"io/ioutil"
+	"bytes"
  	"os"
  )
 
@@ -180,11 +180,17 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
  }
 
  func readinfofile(name string) ([]zonetime, bool) {
-	buf, err := ioutil.ReadFile(name)
+	var b bytes.Buffer
+
+	f, err := os.Open(name)
  	if err != nil {
  		return nil, false
  	}
-	return parseinfo(buf)
+	defer f.Close()
+	if _, err := b.ReadFrom(f); err != nil {
+		return nil, false
+	}
+	return parseinfo(b.Bytes())
  }
 
  func setupTestingZone() {

コアとなるコードの解説

src/pkg/io/ioutil/tempfile.go

  • 変更前: reseed関数内でos.Time()を呼び出し、その戻り値である秒とナノ秒を使って乱数のシードを生成していました。
  • 変更後: os.Time()の代わりにtime.Nanoseconds()を呼び出すように変更されました。time.Nanoseconds()は直接ナノ秒単位のint64を返すため、sec*1e9 + nsecのような計算が不要になり、コードがより簡潔になりました。osパッケージのインポートが削除され、timeパッケージが新しくインポートされています。

src/pkg/net/hosts.go

  • 変更前: readHosts関数内でos.Time()を呼び出し、現在時刻の取得とキャッシュの更新時刻の設定を行っていました。
  • 変更後: os.Time()の代わりにtime.Seconds()を呼び出すように変更されました。time.Seconds()は秒単位のint64を返すため、キャッシュの有効期限(cacheMaxAgeが秒単位)との比較に適しています。ここでもosパッケージのインポートが削除され、timeパッケージが新しくインポートされています。

src/pkg/time/zoneinfo_unix.go

  • 変更前: readinfofile関数内でio/ioutil.ReadFile(name)を呼び出し、指定されたファイルの内容を一度に読み込んでいました。
  • 変更後:
    1. io/ioutilパッケージのインポートが削除され、代わりにbytesパッケージがインポートされました。
    2. ioutil.ReadFileの代わりに、bytes.Bufferos.Openを組み合わせた手動でのファイル読み込みロジックが導入されました。
    3. var b bytes.Bufferで新しいバイトバッファを初期化します。
    4. f, err := os.Open(name)でファイルを読み取り用に開きます。エラーハンドリングも適切に行われています。
    5. defer f.Close()で関数終了時にファイルが確実に閉じられるようにします。
    6. if _, err := b.ReadFrom(f); err != nilで、開いたファイルfからbytes.Buffer bへデータを読み込みます。ReadFromメソッドはio.Readerインターフェースを引数にとり、ファイルの内容を効率的にバッファにコピーします。
    7. 最後にparseinfo(b.Bytes())を呼び出し、バッファの内容をバイトスライスとして渡しています。

このtime/zoneinfo_unix.goの変更が、timeパッケージとio/ioutilパッケージ間の依存関係の循環を解消する上で最も重要な部分です。io/ioutil.ReadFileへの依存をなくすことで、timeパッケージはio/ioutilをインポートする必要がなくなり、循環参照が断ち切られました。

関連リンク

参考にした情報源リンク

  • Go言語の公式リポジトリのコミット履歴
  • Go言語のパッケージドキュメント (特にos, time, io/ioutil, bytes, netパッケージの当時のバージョンに関する情報)
  • Go言語の設計に関する議論やメーリングリストのアーカイブ (os.Timeの廃止や依存関係の循環に関する議論を特定するため)
  • Go言語のバージョンごとの変更履歴 (特にGo 1.0リリース前の開発段階の変更点)

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

このコミットは、Go言語の標準ライブラリにおいて、os.Time関数の使用を廃止し、関連する依存関係の循環問題を解決するための変更です。具体的には、io/ioutilパッケージとnetパッケージからos.Timeへの依存を取り除き、timeパッケージ内でのio/ioutilの使用をbytes.Bufferを用いたファイル読み込みに置き換えることで、依存関係の循環を解消しています。

コミット

commit a6c501e4b1c0bc21e85a51ef62af65c4f284fff1
Author: Anthony Martin <ality@pbrane.org>
Date:   Fri Nov 11 14:40:41 2011 -0500

    net, io/ioutil: remove use of os.Time
    
    I had to replace the single use of io/ioutil
    in the time package with a bytes.Buffer since
    there would've been a dependency cycle.
    
    There are no other uses of os.Time.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5372054

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

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

元コミット内容

このコミットの元の内容は、os.Time関数の使用をnetパッケージとio/ioutilパッケージから削除することです。また、timeパッケージがio/ioutilを使用している唯一の箇所をbytes.Bufferに置き換えることで、timeパッケージとio/ioutil間の依存関係の循環を解消しています。コミットメッセージは、os.Timeの他の使用箇所がないことを明記しています。

変更の背景

この変更の背景には、Go言語の標準ライブラリ設計における依存関係の整理と、より適切なAPIの選択があります。

  1. os.Timeの廃止: os.Timeは、Goの初期バージョンに存在した関数で、システム時刻を秒とナノ秒で返すものでした。しかし、Goの設計思想として、特定の機能はより専門的なパッケージに集約されるべきという考え方があります。時刻に関する機能はtimeパッケージに集約されるべきであり、osパッケージが時刻情報を提供する役割を持つのは適切ではないと判断されたと考えられます。これにより、timeパッケージが提供するより高レベルで柔軟な時刻API(例: time.Now(), time.Nanoseconds(), time.Seconds())への移行が促されました。

  2. 依存関係の循環 (Dependency Cycle): コミットメッセージで言及されている「dependency cycle」は、Goのパッケージ管理において非常に重要な問題です。Goでは、パッケージAがパッケージBをインポートし、同時にパッケージBがパッケージAをインポートするような循環参照は許されません。 このコミットでは、timeパッケージがio/ioutilをインポートし、同時にio/ioutilosをインポートし、さらにostimeパッケージの機能(os.Time)に依存しているという状況が発生していた可能性があります。具体的には、timeパッケージがio/ioutil.ReadFileを使ってタイムゾーン情報を読み込み、一方でio/ioutilos.Timeを使って一時ファイル名を生成していたため、time -> io/ioutil -> os -> timeという循環参照が生じていたと推測されます。 このような循環参照は、コンパイルエラーを引き起こすか、あるいはコードの理解と保守を極めて困難にします。このコミットは、timeパッケージがio/ioutilに依存する部分を、より低レベルなos.Openbytes.Bufferを使った直接的なファイル読み込みに置き換えることで、この循環を断ち切っています。

  3. APIの洗練: os.Timeのような低レベルな時刻取得APIは、より抽象化されたtimeパッケージの関数に置き換えられることで、コードの可読性と堅牢性が向上します。time.Nanoseconds()time.Seconds()は、それぞれナノ秒単位、秒単位のUnixエポックからの経過時間を返すため、os.Timeが返していた秒とナノ秒のタプルを扱うよりもシンプルです。

前提知識の解説

このコミットを理解するためには、以下のGo言語の基本的な概念と標準ライブラリの知識が必要です。

  • Go言語のパッケージシステム: Goのコードはパッケージに分割され、importキーワードを使って他のパッケージの機能を利用します。Goのビルドシステムは、パッケージ間の依存関係を解決しますが、循環参照は許可されません。
  • osパッケージ: オペレーティングシステムと対話するための機能(ファイル操作、プロセス管理、環境変数など)を提供します。
  • timeパッケージ: 時刻と日付を扱うための機能(現在時刻の取得、時刻のフォーマット、時間間隔の計算など)を提供します。
  • io/ioutilパッケージ: I/O操作に関するユーティリティ関数を提供します。特にReadFileは、ファイルの内容をバイトスライスとして一度に読み込む便利な関数です。
  • netパッケージ: ネットワークI/Oに関する機能(TCP/UDP接続、DNSルックアップなど)を提供します。
  • bytesパッケージ: バイトスライスを操作するための機能を提供します。bytes.Bufferは、可変長のバイトバッファとして機能し、io.Readerio.Writerインターフェースを実装しているため、I/O操作の中間バッファとして非常に便利です。
  • os.Time() (旧API): Goの初期バージョンに存在した関数で、func Time() (sec, nsec int64, mono int64) のように、Unixエポックからの秒数とナノ秒数を返していました。このコミットの時点では既に非推奨または削除の対象となっていました。
  • time.Nanoseconds() / time.Seconds(): timeパッケージが提供する関数で、それぞれUnixエポックからの経過時間をナノ秒単位、秒単位でint64として返します。これらはos.Time()の代替として使用されます。
  • os.Open(): osパッケージの関数で、指定されたパスのファイルを読み取り用に開きます。ファイルディスクリプタ(*os.File)を返します。
  • io.Readerインターフェース: Read(p []byte) (n int, err error)メソッドを持つインターフェースで、データを読み込むことができる型が実装します。
  • bytes.Buffer.ReadFrom(r io.Reader): bytes.Bufferのメソッドで、io.ReaderからEOFに達するまでデータを読み込み、バッファに追加します。

技術的詳細

このコミットの技術的詳細は、Go言語のパッケージ設計原則と、特定のAPIの進化に焦点を当てています。

os.Timeの廃止と代替

os.Timeは、Goの初期段階でシステム時刻を取得するために使用されていましたが、その役割はより専門的なtimeパッケージに移行されました。os.Timeは、秒とナノ秒を別々に返す形式でしたが、timeパッケージのtime.Nanoseconds()time.Seconds()は、単一のint64値としてナノ秒または秒単位のUnixエポックからの経過時間を返します。これにより、時刻の扱いがより統一され、簡潔になりました。

  • io/ioutil/tempfile.goでの変更:

    • 変更前: sec, nsec, _ := os.Time() を使用して時刻を取得し、uint32(sec*1e9 + nsec + int64(os.Getpid())) で乱数のシードを生成していました。
    • 変更後: uint32(time.Nanoseconds() + int64(os.Getpid())) を使用しています。time.Nanoseconds()は直接ナノ秒単位のint64を返すため、秒とナノ秒を結合する手間が省かれ、コードが簡潔になっています。
  • net/hosts.goでの変更:

    • 変更前: now, _, _ := os.Time() を使用して現在時刻を取得し、hosts.time, _, _ = os.Time() でキャッシュの更新時刻を設定していました。
    • 変更後: now := time.Seconds()hosts.time = time.Seconds() を使用しています。ここでは、キャッシュの有効期限を秒単位で管理しているため、time.Seconds()が適切です。

依存関係の循環の解消

最も重要な変更は、src/pkg/time/zoneinfo_unix.goにおけるio/ioutil.ReadFileの使用の置き換えです。

  • 問題の構造:

    • timeパッケージはタイムゾーン情報をファイルから読み込むためにio/ioutil.ReadFileを使用。
    • io/ioutilパッケージは一時ファイル名を生成するためにos.Timeを使用。
    • os.Timeは、timeパッケージの内部実装に依存している(または、osパッケージがtimeパッケージをインポートしている)。
    • これにより、time -> io/ioutil -> os -> timeという循環参照が発生し、Goのビルドシステムがこれを解決できませんでした。
  • 解決策:

    • io/ioutil.ReadFile(name)の代わりに、より低レベルなファイル操作とbytes.Bufferを組み合わせることで、timeパッケージからio/ioutilへの直接的な依存を解消しました。
    • 具体的には、os.Open(name)でファイルを開き、そのファイルディスクリプタ(*os.File)をbytes.BufferReadFromメソッドに渡して内容を読み込んでいます。
    • bytes.Bufferio.Readerインターフェースを実装しているため、f.ReadFrom(f)のように*os.Fileから直接データを読み込むことができます。これにより、io/ioutilを介さずにファイルの内容をメモリにロードすることが可能になります。

この変更により、timeパッケージはosパッケージとbytesパッケージにのみ依存するようになり、io/ioutilへの依存がなくなりました。結果として、循環参照が解消され、パッケージ構造がより健全になりました。

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

src/pkg/io/ioutil/tempfile.go

--- a/src/pkg/io/ioutil/tempfile.go
+++ b/src/pkg/io/ioutil/tempfile.go
@@ -8,6 +8,7 @@ import (
  	"os"
  	"path/filepath"
  	"strconv"
+	"time"
 )
 
 // Random number state, accessed without lock; racy but harmless.
@@ -17,8 +18,7 @@ var rand uint32
 
 func reseed() uint32 {
-	sec, nsec, _ := os.Time()
-	return uint32(sec*1e9 + nsec + int64(os.Getpid()))
+	return uint32(time.Nanoseconds() + int64(os.Getpid()))
 }
 
 func nextSuffix() string {

src/pkg/net/hosts.go

--- a/src/pkg/net/hosts.go
+++ b/src/pkg/net/hosts.go
@@ -7,8 +7,8 @@
  package net
 
  import (
-	"os"
  	"sync"
+	"time"
  )
 
  const cacheMaxAge = int64(300) // 5 minutes.
@@ -26,7 +26,7 @@ var hosts struct {
  }
 
  func readHosts() {
-	now, _, _ := os.Time()
+	now := time.Seconds()
  	hp := hostsPath
  	if len(hosts.byName) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp {
  		hs := make(map[string][]string)
@@ -51,7 +51,7 @@ func readHosts() {
  			}
  		}
  		// Update the data cache.
-		hosts.time, _, _ = os.Time()
+		hosts.time = time.Seconds()
  		hosts.path = hp
  		hosts.byName = hs
  		hosts.byAddr = is

src/pkg/time/zoneinfo_unix.go

--- a/src/pkg/time/zoneinfo_unix.go
+++ b/src/pkg/time/zoneinfo_unix.go
@@ -12,7 +12,7 @@
  package time
 
  import (
-	"io/ioutil"
+	"bytes"
  	"os"
  )
 
@@ -180,11 +180,17 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
  }
 
  func readinfofile(name string) ([]zonetime, bool) {
-	buf, err := ioutil.ReadFile(name)
+	var b bytes.Buffer
+
+	f, err := os.Open(name)
  	if err != nil {
  		return nil, false
  	}
-	return parseinfo(buf)
+	defer f.Close()
+	if _, err := b.ReadFrom(f); err != nil {
+		return nil, false
+	}
+	return parseinfo(b.Bytes())
  }
 
  func setupTestingZone() {

コアとなるコードの解説

src/pkg/io/ioutil/tempfile.go

  • 変更前: reseed関数内でos.Time()を呼び出し、その戻り値である秒とナノ秒を使って乱数のシードを生成していました。
  • 変更後: os.Time()の代わりにtime.Nanoseconds()を呼び出すように変更されました。time.Nanoseconds()は直接ナノ秒単位のint64を返すため、sec*1e9 + nsecのような計算が不要になり、コードがより簡潔になりました。osパッケージのインポートが削除され、timeパッケージが新しくインポートされています。

src/pkg/net/hosts.go

  • 変更前: readHosts関数内でos.Time()を呼び出し、現在時刻の取得とキャッシュの更新時刻の設定を行っていました。
  • 変更後: os.Time()の代わりにtime.Seconds()を呼び出すように変更されました。time.Seconds()は秒単位のint64を返すため、キャッシュの有効期限(cacheMaxAgeが秒単位)との比較に適しています。ここでもosパッケージのインポートが削除され、timeパッケージが新しくインポートされています。

src/pkg/time/zoneinfo_unix.go

  • 変更前: readinfofile関数内でio/ioutil.ReadFile(name)を呼び出し、指定されたファイルの内容を一度に読み込んでいました。
  • 変更後:
    1. io/ioutilパッケージのインポートが削除され、代わりにbytesパッケージがインポートされました。
    2. ioutil.ReadFileの代わりに、bytes.Bufferos.Openを組み合わせた手動でのファイル読み込みロジックが導入されました。
    3. var b bytes.Bufferで新しいバイトバッファを初期化します。
    4. f, err := os.Open(name)でファイルを読み取り用に開きます。エラーハンドリングも適切に行われています。
    5. defer f.Close()で関数終了時にファイルが確実に閉じられるようにします。
    6. if _, err := b.ReadFrom(f); err != nilで、開いたファイルfからbytes.Buffer bへデータを読み込みます。ReadFromメソッドはio.Readerインターフェースを引数にとり、ファイルの内容を効率的にバッファにコピーします。
    7. 最後にparseinfo(b.Bytes())を呼び出し、バッファの内容をバイトスライスとして渡しています。

このtime/zoneinfo_unix.goの変更が、timeパッケージとio/ioutilパッケージ間の依存関係の循環を解消する上で最も重要な部分です。io/ioutil.ReadFileへの依存をなくすことで、timeパッケージはio/ioutilをインポートする必要がなくなり、循環参照が断ち切られました。

関連リンク

参考にした情報源リンク

  • Go言語の公式リポジトリのコミット履歴
  • Go言語のパッケージドキュメント (特にos, time, io/ioutil, bytes, netパッケージの当時のバージョンに関する情報)
  • Go言語の設計に関する議論やメーリングリストのアーカイブ (os.Timeの廃止や依存関係の循環に関する議論を特定するため)
  • Go言語のバージョンごとの変更履歴 (特にGo 1.0リリース前の開発段階の変更点)
  • Web検索: "Go os.Time deprecated 2011" (os.Timeの廃止時期とtimeパッケージへの移行に関する情報確認のため)