[インデックス 10730] ファイルの概要
このコミットは、Go言語の標準ライブラリ内の様々な箇所で、時間間隔を表すtime.Duration
型の使用方法を整理し、改善することを目的としています。具体的には、マジックナンバーとして直接記述されていたナノ秒単位の整数値や浮動小数点数値を、time.Second
やtime.Millisecond
といったtime.Duration
型の定数と乗算する形式に置き換えることで、コードの可読性、保守性、および型安全性を向上させています。
コミット
commit 3dbecd592b8bf084770c8d6f38bd8094f74b8258
Author: David Symonds <dsymonds@golang.org>
Date: Tue Dec 13 10:42:56 2011 +1100
various: a grab-bag of time.Duration cleanups.
R=adg, r, rsc
CC=golang-dev
https://golang.org/cl/5475069
---
src/cmd/godoc/index.go | 3 ++-
src/pkg/exp/inotify/inotify_linux_test.go | 6 +++---
src/pkg/exp/norm/normregtest.go | 2 +-\
src/pkg/exp/winfsnotify/winfsnotify_test.go | 4 ++--
src/pkg/go/printer/printer_test.go | 2 +-\
src/pkg/io/pipe_test.go | 2 +-\
src/pkg/net/http/doc.go | 4 ++--
src/pkg/net/http/serve_test.go | 2 +-\
src/pkg/net/http/server.go | 14 +++++++-------
src/pkg/net/http/transport_test.go | 2 +-\
src/pkg/net/rpc/server_test.go | 7 +++----
src/pkg/old/netchan/common.go | 4 ++--
src/pkg/old/netchan/import.go | 2 +-\
src/pkg/old/netchan/netchan_test.go | 6 +++---
14 files changed, 30 insertions(+), 30 deletions(-)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3dbecd592b8bf084770c8d6f38bd8094f74b8258
元コミット内容
various: a grab-bag of time.Duration cleanups.
R=adg, r, rsc
CC=golang-dev
https://golang.org/cl/5475069
変更の背景
このコミットが行われた背景には、Go言語のコードベース全体における時間間隔の表現の一貫性と明確性の向上という目的があります。以前のコードでは、時間間隔をナノ秒単位の整数値(例: 1e9
は1秒を表す)や浮動小数点数で直接表現している箇所が散見されました。このような記述方法は、以下の問題を引き起こす可能性がありました。
- 可読性の低下:
1e9
が1秒を意味することを即座に理解できない読者にとっては、コードの意図が不明瞭になります。特に、100e6
のような値は、それが100ミリ秒なのか、100マイクロ秒なのか、あるいは別の単位なのかを判断するために計算が必要になります。 - エラーの誘発: 単位の誤解や計算ミスにより、意図しない時間間隔が設定されるリスクがありました。例えば、ミリ秒と秒を混同するなどのヒューマンエラーが発生しやすくなります。
- 型安全性の欠如: 整数値や浮動小数点数は、時間間隔以外の目的でも使用される汎用的な型であるため、コンパイラが時間間隔としての誤用を検出することが困難でした。
- 保守性の問題: 時間間隔の単位を変更する場合(例えば、ミリ秒からマイクロ秒へ)、すべてのマジックナンバーを手動で計算し直す必要があり、変更漏れや新たなバグの原因となる可能性がありました。
これらの問題を解決し、より堅牢で理解しやすいコードベースを構築するために、time.Duration
型とその関連定数を用いた明示的な時間間隔の表現への移行が推進されました。
前提知識の解説
time.Duration
型
Go言語の標準ライブラリであるtime
パッケージには、時間間隔を表すためのDuration
型が定義されています。
- 定義:
type Duration int64
Duration
型は、内部的にはint64
型として実装されており、ナノ秒単位で時間間隔を保持します。
- 定数:
time
パッケージは、一般的な時間単位に対応する定数を提供しています。time.Nanosecond
time.Microsecond
time.Millisecond
time.Second
time.Minute
time.Hour
- 使用方法: これらの定数と整数値を乗算することで、人間が理解しやすい形式で時間間隔を表現できます。
- 例:
1 * time.Second
(1秒),500 * time.Millisecond
(500ミリ秒),2 * time.Hour
(2時間)
- 例:
- 利点:
- 可読性: コードを見ただけで、それが何秒、何ミリ秒を意味するのかが明確になります。
- 型安全性:
Duration
型は時間間隔専用の型であるため、誤った単位での計算や、時間間隔ではない値との混同を防ぐことができます。コンパイラが型ミスマッチを検出してくれるため、開発段階でバグを発見しやすくなります。 - 保守性: 時間の単位を変更する際も、定数名を変更するだけで済み、計算し直す必要がありません。
time.Sleep()
と time.After()
time.Sleep(d Duration)
: 指定された期間d
だけ現在のゴルーチンを一時停止させます。time.After(d Duration) <-chan Time
: 指定された期間d
が経過した後に、現在の時刻を送信するチャネルを返します。これは、タイムアウト処理などでよく使用されます。
これらの関数は、引数としてtime.Duration
型を期待するため、以前のコードではナノ秒単位の整数値を直接渡していましたが、このコミットによりtime.Duration
定数を用いたより明確な記述に統一されました。
技術的詳細
このコミットの技術的な詳細は、Go言語の型システムと、時間管理におけるベストプラクティスへの準拠に集約されます。
-
マジックナンバーの排除:
1e9
や1000e6
のような数値リテラルは、その意味が文脈に依存するため「マジックナンバー」と呼ばれます。これらの数値がナノ秒を表すことを知らなければ、コードの意図を正確に把握することは困難です。1 * time.Second
や100 * time.Millisecond
と記述することで、数値が表す時間単位が明示され、コードの自己文書化能力が向上します。
-
型安全性の強化:
time.Duration
型はint64
のエイリアスですが、Goの型システムはエイリアス型を元の型とは異なる型として扱います。これにより、time.Duration
を期待する関数に誤って単なるint64
を渡そうとすると、コンパイルエラーが発生します。- 例えば、
time.Sleep(100)
と書くとコンパイルエラーになりますが、time.Sleep(100 * time.Millisecond)
は正しくコンパイルされます。これにより、開発者は意図しない単位の誤用を防ぐことができます。
-
一貫性の確保:
- Goの標準ライブラリ全体で
time.Duration
の使用を統一することで、コードベース全体の一貫性が向上します。これにより、異なるモジュールやパッケージ間での時間間隔の扱いに混乱が生じることを防ぎ、開発者がGoの慣用的な書き方に慣れるのを助けます。
- Goの標準ライブラリ全体で
-
将来的な互換性と保守性:
- もし将来的に
time.Duration
の内部表現や、時間単位の計算方法に変更があったとしても、time.Second
のような定数を使用していれば、コードの変更は最小限で済みます。マジックナンバーを使用している場合、広範囲にわたる手動での修正が必要になる可能性があります。
- もし将来的に
-
net/http
パッケージにおけるタイムアウト設定の改善:net/http
パッケージのServer
構造体におけるReadTimeout
とWriteTimeout
フィールドの型がint64
からtime.Duration
に変更されました。- これにより、HTTPサーバーのタイムアウト設定がより直感的になり、
10 * time.Second
のように直接time.Duration
値で指定できるようになりました。 - 内部的には、
SetReadTimeout
やSetWriteTimeout
メソッドに渡す際に、time.Duration
型のNanoseconds()
メソッドを使用してナノ秒単位のint64
値に変換しています。これは、これらのメソッドが元々int64
(ナノ秒)を期待していたため、既存のAPIとの互換性を保ちつつ、外部からの設定をtime.Duration
で受け入れるように変更されたことを示しています。
コアとなるコードの変更箇所
このコミットでは、主に以下のパターンでコードが変更されています。
-
time.Sleep()
およびtime.After()
の引数:- 変更前:
time.Sleep(1000e6)
(1000ミリ秒 = 1秒) - 変更後:
time.Sleep(1 * time.Second)
- 変更前:
time.After(1e9)
(1秒) - 変更後:
time.After(1 * time.Second)
- 変更前:
time.Sleep(50e6)
(50ミリ秒) - 変更後:
time.Sleep(50 * time.Millisecond)
- 変更前:
-
net/http
パッケージのServer
構造体のフィールド型変更:- 変更前:
type Server struct { // ... ReadTimeout int64 // the net.Conn.SetReadTimeout value for new connections WriteTimeout int64 // the net.Conn.SetWriteTimeout value for new connections // ... }
- 変更後:
type Server struct { // ... ReadTimeout time.Duration // the net.Conn.SetReadTimeout value for new connections WriteTimeout time.Duration // the net.Conn.SetWriteTimeout value for new connections // ... }
- 変更前:
-
net/http
パッケージのServer
構造体フィールドの利用箇所:- 変更前:
if srv.ReadTimeout != 0 { rw.SetReadTimeout(srv.ReadTimeout) } if srv.WriteTimeout != 0 { rw.SetWriteTimeout(srv.WriteTimeout) }
- 変更後:
if srv.ReadTimeout != 0 { rw.SetReadTimeout(srv.ReadTimeout.Nanoseconds()) } if srv.WriteTimeout != 0 { rw.SetWriteTimeout(srv.WriteTimeout.Nanoseconds()) }
- 変更前:
-
その他の時間間隔の表現:
src/cmd/godoc/index.go
:NewThrottle
関数の引数で0.1e9
が100*time.Millisecond
に変更。src/pkg/net/rpc/server_test.go
:second = 1e9
という定数定義が削除され、直接time.Second
が使用されるように変更。
コアとなるコードの解説
time.Sleep()
/ time.After()
の引数変更
これは最も直接的な変更であり、時間間隔の意図を明確にするためのものです。
1000e6
は1000 * 10^6
ナノ秒、つまり10^9
ナノ秒 = 1秒を意味します。これを1 * time.Second
と書くことで、誰が見ても1秒であることが一目瞭然になります。- 同様に、
50e6
は50ミリ秒を意味し、50 * time.Millisecond
とすることで、単位が明確になります。 この変更は、コードの可読性を劇的に向上させ、将来的な誤解やバグの発生を防ぐ上で非常に重要です。
net/http/server.go
における Server
構造体の変更
この変更は、HTTPサーバーのタイムアウト設定のインターフェースを改善するものです。
ReadTimeout
とWriteTimeout
の型をint64
からtime.Duration
に変更することで、http.Server
のインスタンスを作成する際に、タイムアウト値を10 * time.Second
のように、より自然なGoの慣用句で指定できるようになりました。- 内部的には、
net.Conn.SetReadTimeout
やSetWriteTimeout
が依然としてナノ秒単位のint64
を期待しているため、srv.ReadTimeout.Nanoseconds()
のようにDuration
型のNanoseconds()
メソッドを呼び出してint64
に変換しています。これは、外部からの設定をよりGoらしい方法で受け入れつつ、既存の低レベルAPIとの互換性を維持するための設計パターンです。このアプローチにより、APIの使いやすさと内部実装の効率性の両立が図られています。
net/rpc/server_test.go
からの second
定数削除
const second = 1e9
という定義は、1e9
が1秒を意味するというマジックナンバーを定数としてラップしたものでした。しかし、time.Second
というより明確で型安全な定数が既に存在するため、このカスタム定数は冗長であり、混乱を招く可能性がありました。この定数を削除し、直接time.Second
を使用するように変更することで、コードの重複を避け、Go標準ライブラリの慣用的な表現に統一されました。
これらの変更は、Go言語の設計思想である「明確さ (clarity)」と「シンプルさ (simplicity)」を追求したものであり、コードベース全体の品質向上に貢献しています。
関連リンク
- Go言語
time
パッケージのドキュメント: https://pkg.go.dev/time - Go言語
net/http
パッケージのドキュメント: https://pkg.go.dev/net/http
参考にした情報源リンク
- 特になし (Go言語の標準ライブラリのドキュメントと、一般的なGoのコーディング規約に基づいています。)