[インデックス 10662] ファイルの概要
このコミットは、Go言語の標準ライブラリにおける time
パッケージの AfterFunc
関数が、時間間隔の指定に int64
型のナノ秒ではなく、より型安全で表現力の高い time.Duration
型を使用するように変更されたものです。これにより、コードの可読性と堅牢性が向上し、時間単位の誤用を防ぐことができます。
コミット
commit 2949f3b659f2efb161efca19ff92398fbf37e081
Author: David Symonds <dsymonds@golang.org>
Date: Thu Dec 8 15:42:44 2011 +1100
time: use Duration for AfterFunc.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5465043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2949f3b659f2efb161efca19ff92398fbf37e081
元コミット内容
time: use Duration for AfterFunc.
このコミットは、time
パッケージの AfterFunc
関数が、時間指定に time.Duration
型を使用するように変更することを目的としています。
変更の背景
Go言語の初期のバージョンでは、時間間隔をナノ秒単位の int64
型で表現することが一般的でした。しかし、int64
は単なる整数型であるため、それが時間間隔を表すのか、あるいは他の数値(例えば、バイト数やカウントなど)を表すのかがコード上で不明瞭になるという問題がありました。これにより、開発者が誤った単位で数値を渡してしまう可能性や、コードの意図が伝わりにくくなるという課題がありました。
time.Duration
型は、Go言語の time
パッケージで定義されているカスタム型であり、時間間隔を明示的に表現するために導入されました。この型を使用することで、コンパイラが型チェックを行い、誤った型の値が渡されることを防ぐことができます。また、コードを読む人にとっても、その値が時間間隔であることを一目で理解できるようになります。
このコミットは、このような背景から、AfterFunc
関数がより安全で表現力の高い time.Duration
型を使用するように変更することで、Go言語のAPI設計の一貫性と堅牢性を向上させることを目的としています。
前提知識の解説
Go言語の time
パッケージ
Go言語の time
パッケージは、時間に関する機能を提供する標準ライブラリです。時刻の表現 (time.Time
)、時間間隔の表現 (time.Duration
)、タイマー、ティック、日付のフォーマットなど、様々な機能が含まれています。
time.Duration
型
time.Duration
は、Go言語の time
パッケージで定義されている型であり、時間間隔を表します。これは int64
のエイリアスとして定義されており、内部的にはナノ秒単位で時間を保持します。しかし、単なる int64
とは異なり、time.Duration
型は時間間隔であることを明示的に示し、コンパイラによる型チェックの恩恵を受けます。
time.Duration
型は、以下のような定数とメソッドを提供します。
- 定数:
time.Nanosecond
,time.Microsecond
,time.Millisecond
,time.Second
,time.Minute
,time.Hour
など、様々な時間単位を表す定数が定義されています。これにより、5 * time.Second
のように、人間が理解しやすい形で時間間隔を記述できます。 - メソッド:
Seconds()
,Milliseconds()
,String()
など、時間間隔を異なる単位で取得したり、文字列として表現したりするためのメソッドが提供されています。
time.AfterFunc
関数
time.AfterFunc
は、指定された時間間隔が経過した後に、別のゴルーチンで関数を実行するための関数です。この関数は *time.Timer
を返します。このタイマーは、Stop()
メソッドを呼び出すことで、関数が実行される前にキャンセルすることができます。
変更前は、AfterFunc
の第一引数は ns int64
であり、ナノ秒単位の整数値を受け取っていました。変更後は d Duration
となり、time.Duration
型の値を受け取るようになります。
技術的詳細
このコミットの主要な変更点は、time.AfterFunc
関数のシグネチャと、それに伴う内部実装および呼び出し箇所の修正です。
time.AfterFunc
のシグネチャ変更
変更前:
func AfterFunc(ns int64, f func()) *Timer
変更後:
func AfterFunc(d Duration, f func()) *Timer
この変更により、AfterFunc
を呼び出す際には、int64
のナノ秒ではなく、time.Duration
型の値を渡すことが必須となります。これにより、例えば time.AfterFunc(5, func(){})
のような記述はコンパイルエラーとなり、time.AfterFunc(5 * time.Second, func(){})
のように明示的に時間単位を指定する必要が生じます。これは、開発者が意図しない時間単位で関数を呼び出すことを防ぐ上で非常に重要です。
内部実装の変更
time.AfterFunc
の内部では、タイマーの when
フィールド(タイマーが発火する時刻)を計算する際に、引数で受け取った時間間隔をナノ秒に変換して使用します。
変更前:
when: nano() + ns,
変更後:
when: nano() + int64(d),
time.Duration
型は内部的に int64
でナノ秒を保持しているため、int64(d)
と型変換することで、以前と同様にナノ秒単位の値を when
フィールドに加算しています。この変更は、外部APIの型安全性を高めつつ、内部の処理ロジックは大きく変えることなく実現されています。
呼び出し箇所の変更
time.AfterFunc
のシグネチャ変更に伴い、この関数を呼び出している既存のコードも修正されています。主にテストコードにおいて、1e9
(10億ナノ秒) のような int64
リテラルが 1 * time.Second
や time.Second
のように time.Duration
型の定数と演算子を組み合わせた表現に置き換えられています。これにより、コードの意図がより明確になり、時間単位の誤解がなくなります。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルは以下の通りです。
src/pkg/net/http/serve_test.go
:http
パッケージのテストファイルで、goTimeout
関数やtime.After
の呼び出し箇所がtime.Duration
を使用するように変更されています。src/pkg/testing/testing.go
:testing
パッケージの内部で、テストのタイムアウト処理に使用されるtime.AfterFunc
の呼び出しが変更されています。src/pkg/time/sleep.go
:time
パッケージのAfterFunc
関数のシグネチャと内部実装が変更されています。これがこのコミットの核心部分です。src/pkg/time/sleep_test.go
:time
パッケージのテストファイルで、AfterFunc
やNewTimer
の呼び出し箇所がtime.Duration
を使用するように変更されています。
コアとなるコードの解説
src/pkg/time/sleep.go
の変更
--- a/src/pkg/time/sleep.go
+++ b/src/pkg/time/sleep.go
@@ -72,13 +72,13 @@ func After(d Duration) <-chan Time {
return NewTimer(d).C
}
-// AfterFunc waits at least ns nanoseconds before calling f
+// AfterFunc waits for the duration to elapse and then calls f
// in its own goroutine. It returns a Timer that can
// be used to cancel the call using its Stop method.
-func AfterFunc(ns int64, f func()) *Timer {
+func AfterFunc(d Duration, f func()) *Timer {
t := &Timer{
r: runtimeTimer{
- when: nano() + ns,
+ when: nano() + int64(d),
f: goFunc,
arg: f,
},
この差分は、time.AfterFunc
関数の定義そのものです。
- 関数のコメントが「
AfterFunc waits at least ns nanoseconds before calling f
」から「AfterFunc waits for the duration to elapse and then calls f
」に変更され、引数がナノ秒ではなく期間(Duration)であることを明確にしています。 - 関数のシグネチャが
func AfterFunc(ns int64, f func()) *Timer
からfunc AfterFunc(d Duration, f func()) *Timer
に変更されました。これにより、第一引数の型がint64
からtime.Duration
に変わりました。 - 内部の実装で、
when: nano() + ns,
の部分がwhen: nano() + int64(d),
に変更されました。time.Duration
型の変数d
をint64
に明示的に型変換することで、内部的には引き続きナノ秒単位の整数値として扱っています。これはtime.Duration
がint64
のエイリアスであるため可能です。
src/pkg/net/http/serve_test.go
の変更例
--- a/src/pkg/net/http/serve_test.go
+++ b/src/pkg/net/http/serve_test.go
@@ -361,7 +361,7 @@ func TestIdentityResponse(t *testing.T) {
// The ReadAll will hang for a failing test, so use a Timer to
// fail explicitly.
- goTimeout(t, 2e9, func() {
+ goTimeout(t, 2*time.Second, func() {
got, _ := ioutil.ReadAll(conn)
expectedSuffix := "\r\n\r\ntoo short"
if !strings.HasSuffix(string(got), expectedSuffix) {
@@ -395,7 +395,7 @@ func testTcpConnectionCloses(t *testing.T, req string, h Handler) {
success := make(chan bool)
go func() {
select {
- case <-time.After(5e9):
+ case <-time.After(5 * time.Second):
t.Fatal("body not closed after 5s")
case <-success:
}
この差分は、http
パッケージのテストコードにおける変更例です。
goTimeout
関数の呼び出しで、第二引数が2e9
(20億ナノ秒) から2*time.Second
に変更されています。goTimeout
関数も同様に引数の型がint64
からtime.Duration
に変更されています。time.After
関数の呼び出しで、引数が5e9
から5 * time.Second
に変更されています。time.After
もtime.Duration
を引数に取る関数です。
これらの変更は、time.Duration
型の導入によって、時間間隔の表現がより明確になり、コードの可読性と保守性が向上したことを示しています。
関連リンク
- Go言語の
time
パッケージに関する公式ドキュメント: https://pkg.go.dev/time - このコミットのGo CL (Code Review) ページ: https://golang.org/cl/5465043
参考にした情報源リンク
- Go言語の公式ドキュメント (
time
パッケージ) - Go言語のソースコード (特に
src/pkg/time/sleep.go
) - Go言語のコミット履歴 (GitHub)
- Go言語のコードレビューシステム (Gerrit)