[インデックス 19580] ファイルの概要
コミット
commit 5ce98da1a27f55a11107c861633b68760d9d03e6
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Thu Jun 19 22:04:37 2014 -0700
net: simplify code
Single-case select with a non-nil channel is pointless.
LGTM=mikioh.mikioh
R=mikioh.mikioh
CC=golang-codereviews
https://golang.org/cl/103920044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5ce98da1a27f55a11107c861633b68760d9d03e6
元コミット内容
net: simplify code
Single-case select with a non-nil channel is pointless.
LGTM=mikioh.mikioh
R=mikioh.mikioh
CC=golang-codereviews
https://golang.org/cl/103920044
変更の背景
このコミットの主な目的は、Goのnet
パッケージ内のコードを簡素化することです。コミットメッセージに「Single-case select with a non-nil channel is pointless.(nilでないチャネルに対する単一ケースのselectは無意味である)」と明記されている通り、既存のコードには冗長なselect
ステートメントが存在していました。
Goのselect
ステートメントは、複数のチャネル操作の中から準備ができたものを待機し、実行するために使用されます。しかし、select
ステートメントが単一のcase
しか持たず、かつそのcase
がnilではない(つまり、常に操作可能である)チャネルからの読み取りまたは書き込みである場合、そのselect
は通常のチャネル操作と機能的に全く同じになります。このようなselect
は、コードの意図を不明瞭にし、不必要な複雑さを加えるだけで、何のメリットもありません。
この変更は、このような冗長な構造を特定し、より直接的なチャネル操作に置き換えることで、コードの可読性と保守性を向上させることを目的としています。パフォーマンスへの影響はほとんどありませんが、コードベース全体の品質と一貫性を高めるためのクリーンアップ作業の一環と言えます。
前提知識の解説
Goのselect
ステートメント
select
ステートメントは、Goにおける並行処理の強力なプリミティブの一つです。複数のチャネル操作(送受信)を同時に待機し、そのうちのいずれかが準備できた時点でその操作を実行します。
基本的な構文は以下の通りです。
select {
case <-ch1:
// ch1からの受信
case ch2 <- value:
// ch2への送信
default:
// どのチャネルも準備できていない場合に即座に実行される(オプション)
}
select
の動作にはいくつかの重要な特性があります。
- ブロック: どの
case
も準備できていない場合、select
はブロックし、いずれかのcase
が準備できるまで待機します。 - 非ブロック(
default
使用時):default
ケースが存在する場合、どのチャネル操作も即座に実行できない場合でもselect
はブロックせず、default
ケースが実行されます。 - ランダム選択: 複数の
case
が同時に準備できた場合、select
はそれらのうちの1つをランダムに選択して実行します。 - nilチャネル:
select
のcase
でnilチャネルが使用された場合、そのcase
は決して準備ができたと見なされません。これは、特定のチャネル操作を一時的に無効にするために利用されることがあります。
Goのチャネル
チャネルは、Goのゴルーチン(軽量スレッド)間で値を安全に送受信するための通信メカニズムです。チャネルは型付けされており、特定の型の値のみを送受信できます。
- 作成:
make(chan Type)
またはmake(chan Type, capacity)
- 送信:
ch <- value
- 受信:
value := <-ch
または<-ch
(値が不要な場合)
チャネルは、ゴルーチン間の同期にも広く使用されます。
単一ケースのselect
の特性
今回のコミットの核心は、「nilでないチャネルに対する単一ケースのselect
は無意味である」という点です。
select
ステートメントが以下のような形式であるとします。
select {
case result := <-myChannel:
// ...
}
ここでmyChannel
がnilではない場合、このselect
ステートメントは常にmyChannel
からの受信を試みます。これは、result := <-myChannel
という直接的なチャネル受信操作と全く同じ動作をします。select
の持つ「複数の選択肢から一つを選ぶ」という機能が、単一のcase
しかない場合には発揮されず、単なる冗長なラッパーとなってしまうのです。
したがって、このようなコードは簡潔なresult := <-myChannel
に置き換えることができ、コードの意図がより明確になります。
技術的詳細
このコミットは、src/pkg/net/dial.go
ファイル内のdialMulti
関数におけるselect
ステートメントの変更に焦点を当てています。
dialMulti
関数の役割
dialMulti
関数は、Goのnet
パッケージにおいて、複数のネットワークアドレスに対して並行してダイヤル(接続試行)を行うための内部関数です。これは、例えばDNSが複数のIPアドレスを返す場合や、IPv4とIPv6の両方で接続を試みる場合などに使用されます。
この関数は、複数の「レーサー」(racer
)ゴルーチンを起動し、それぞれが独立して接続を試みます。最初に成功した接続、または最初のエラーを報告した接続の結果を待機し、適切な処理を行います。lane
チャネルは、これらのレーサーゴルーチンからの結果(接続またはエラー)を受け取るために使用されます。
変更前のコードの課題
変更前のdialMulti
関数内には、以下のようなループがありました。
for nracers > 0 {
sig <- true // シグナルを送信
select {
case racer := <-lane: // laneチャネルからの受信を待機
if racer.error == nil {
return racer.Conn, nil // 成功した接続を返す
}
lastErr = racer.error
nracers--
}
}
ここで注目すべきは、select
ステートメントがcase racer := <-lane:
という単一のcase
しか持っていない点です。lane
チャネルはdialMulti
関数内で作成され、nilではないことが保証されています。
前述の「単一ケースのselect
の特性」で説明した通り、nilではないチャネルに対する単一ケースのselect
は、通常のチャネル受信操作と全く同じ動作をします。つまり、このselect
ブロックは、単にlane
チャネルからの値が利用可能になるまでブロックし、値を受信するという動作しか行いません。
このselect
の使用は、コードの意図を不明瞭にし、不必要な構文上のオーバーヘッドを生み出していました。select
は通常、複数の選択肢がある場合にその真価を発揮しますが、この場合はその機能が全く活用されていませんでした。
変更による改善
このコミットでは、冗長なselect
ステートメントを削除し、直接的なチャネル受信操作に置き換えることで、コードを簡素化しました。
変更後のコードは以下のようになります。
for nracers > 0 {
sig <- true // シグナルを送信
racer := <-lane // laneチャネルからの受信
if racer.error == nil {
return racer.Conn, nil // 成功した接続を返す
}
lastErr = racer.error
nracers--
}
この変更により、コードはより直接的で読みやすくなりました。lane
チャネルからの受信が、select
という余分な構文なしに明示的に行われるため、開発者はこの行が単にチャネルからの値の到着を待っていることをすぐに理解できます。機能的な動作は変更されていませんが、コードの意図がより明確になり、保守性が向上しました。
コアとなるコードの変更箇所
--- a/src/pkg/net/dial.go
+++ b/src/pkg/net/dial.go
@@ -214,14 +214,12 @@ func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Con
nracers := len(ras)
for nracers > 0 {
sig <- true
- select {
- case racer := <-lane:
- if racer.error == nil {
- return racer.Conn, nil
- }
- lastErr = racer.error
- nracers--
- }
+ racer := <-lane
+ if racer.error == nil {
+ return racer.Conn, nil
+ }
+ lastErr = racer.error
+ nracers--
}
return nil, lastErr
}
コアとなるコードの解説
変更はsrc/pkg/net/dial.go
ファイルのdialMulti
関数内で行われています。
-
変更前:
select { case racer := <-lane: if racer.error == nil { return racer.Conn, nil } lastErr = racer.error nracers-- }
この部分では、
select
ステートメントが使用されており、lane
チャネルからの受信(case racer := <-lane:
)のみを待機しています。lane
チャネルはnilではないため、このselect
は常にlane
チャネルからの値が利用可能になるまでブロックし、その値を受信します。 -
変更後:
racer := <-lane if racer.error == nil { return racer.Conn, nil } lastErr = racer.error nracers--
変更後では、冗長な
select
ステートメントが完全に削除され、racer := <-lane
という直接的なチャネル受信操作に置き換えられています。これにより、コードはより簡潔になり、lane
チャネルからの値の受信が直接的に表現されるようになりました。その後のエラーチェックとカウンタのデクリメントのロジックは変更されていません。
この変更は、機能的な動作を変えることなく、コードの明瞭さと簡潔さを向上させるためのリファクタリングです。
関連リンク
- Go CL 103920044: https://golang.org/cl/103920044
参考にした情報源リンク
- Go言語の公式ドキュメント:
- Go言語のチャネルに関する一般的な情報源。
- Goの
net
パッケージのソースコード(src/pkg/net/dial.go
)。
[インデックス 19580] ファイルの概要
コミット
commit 5ce98da1a27f55a11107c861633b68760d9d03e6
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Thu Jun 19 22:04:37 2014 -0700
net: simplify code
Single-case select with a non-nil channel is pointless.
LGTM=mikioh.mikioh
R=mikioh.mikioh
CC=golang-codereviews
https://golang.org/cl/103920044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5ce98da1a27f55a11107c861633b68760d9d03e6
元コミット内容
net: simplify code
Single-case select with a non-nil channel is pointless.
LGTM=mikioh.mikioh
R=mikioh.mikioh
CC=golang-codereviews
https://golang.org/cl/103920044
変更の背景
このコミットの主な目的は、Goのnet
パッケージ内のコードを簡素化することです。コミットメッセージに「Single-case select with a non-nil channel is pointless.(nilでないチャネルに対する単一ケースのselectは無意味である)」と明記されている通り、既存のコードには冗長なselect
ステートメントが存在していました。
Goのselect
ステートメントは、複数のチャネル操作の中から準備ができたものを待機し、実行するために使用されます。しかし、select
ステートメントが単一のcase
しか持たず、かつそのcase
がnilではない(つまり、常に操作可能である)チャネルからの読み取りまたは書き込みである場合、そのselect
は通常のチャネル操作と機能的に全く同じになります。このようなselect
は、コードの意図を不明瞭にし、不必要な複雑さを加えるだけで、何のメリットもありません。
この変更は、このような冗長な構造を特定し、より直接的なチャネル操作に置き換えることで、コードの可読性と保守性を向上させることを目的としています。パフォーマンスへの影響はほとんどありませんが、コードベース全体の品質と一貫性を高めるためのクリーンアップ作業の一環と言えます。
前提知識の解説
Goのselect
ステートメント
select
ステートメントは、Goにおける並行処理の強力なプリミティブの一つです。複数のチャネル操作(送受信)を同時に待機し、そのうちのいずれかが準備できた時点でその操作を実行します。
基本的な構文は以下の通りです。
select {
case <-ch1:
// ch1からの受信
case ch2 <- value:
// ch2への送信
default:
// どのチャネルも準備できていない場合に即座に実行される(オプション)
}
select
の動作にはいくつかの重要な特性があります。
- ブロック: どの
case
も準備できていない場合、select
はブロックし、いずれかのcase
が準備できるまで待機します。 - 非ブロック(
default
使用時):default
ケースが存在する場合、どのチャネル操作も即座に実行できない場合でもselect
はブロックせず、default
ケースが実行されます。 - ランダム選択: 複数の
case
が同時に準備できた場合、select
はそれらのうちの1つをランダムに選択して実行します。 - nilチャネル:
select
のcase
でnilチャネルが使用された場合、そのcase
は決して準備ができたと見なされません。これは、特定のチャネル操作を一時的に無効にするために利用されることがあります。
Goのチャネル
チャネルは、Goのゴルーチン(軽量スレッド)間で値を安全に送受信するための通信メカニズムです。チャネルは型付けされており、特定の型の値のみを送受信できます。
- 作成:
make(chan Type)
またはmake(chan Type, capacity)
- 送信:
ch <- value
- 受信:
value := <-ch
または<-ch
(値が不要な場合)
チャネルは、ゴルーチン間の同期にも広く使用されます。
単一ケースのselect
の特性
今回のコミットの核心は、「nilでないチャネルに対する単一ケースのselect
は無意味である」という点です。
select
ステートメントが以下のような形式であるとします。
select {
case result := <-myChannel:
// ...
}
ここでmyChannel
がnilではない場合、このselect
ステートメントは常にmyChannel
からの受信を試みます。これは、result := <-myChannel
という直接的なチャネル受信操作と全く同じ動作をします。select
の持つ「複数の選択肢から一つを選ぶ」という機能が、単一のcase
しかない場合には発揮されず、単なる冗長なラッパーとなってしまうのです。
したがって、このようなコードは簡潔なresult := <-myChannel
に置き換えることができ、コードの意図がより明確になります。
技術的詳細
このコミットは、src/pkg/net/dial.go
ファイル内のdialMulti
関数におけるselect
ステートメントの変更に焦点を当てています。
dialMulti
関数の役割
dialMulti
関数は、Goのnet
パッケージにおいて、複数のネットワークアドレスに対して並行してダイヤル(接続試行)を行うための内部関数です。これは、例えばDNSが複数のIPアドレスを返す場合や、IPv4とIPv6の両方で接続を試みる場合などに使用されます。
この関数は、複数の「レーサー」(racer
)ゴルーチンを起動し、それぞれが独立して接続を試みます。最初に成功した接続、または最初のエラーを報告した接続の結果を待機し、適切な処理を行います。lane
チャネルは、これらのレーサーゴルーチンからの結果(接続またはエラー)を受け取るために使用されます。
変更前のコードの課題
変更前のdialMulti
関数内には、以下のようなループがありました。
for nracers > 0 {
sig <- true // シグナルを送信
select {
case racer := <-lane: // laneチャネルからの受信を待機
if racer.error == nil {
return racer.Conn, nil // 成功した接続を返す
}
lastErr = racer.error
nracers--
}
}
ここで注目すべきは、select
ステートメントがcase racer := <-lane:
という単一のcase
しか持っていない点です。lane
チャネルはdialMulti
関数内で作成され、nilではないことが保証されています。
前述の「単一ケースのselect
の特性」で説明した通り、nilではないチャネルに対する単一ケースのselect
は、通常のチャネル受信操作と全く同じ動作をします。つまり、このselect
ブロックは、単にlane
チャネルからの値が利用可能になるまでブロックし、値を受信するという動作しか行いません。
このselect
の使用は、コードの意図を不明瞭にし、不必要な構文上のオーバーヘッドを生み出していました。select
は通常、複数の選択肢がある場合にその真価を発揮しますが、この場合はその機能が全く活用されていませんでした。
変更による改善
このコミットでは、冗長なselect
ステートメントを削除し、直接的なチャネル受信操作に置き換えることで、コードを簡素化しました。
変更後のコードは以下のようになります。
for nracers > 0 {
sig <- true // シグナルを送信
racer := <-lane // laneチャネルからの受信
if racer.error == nil {
return racer.Conn, nil // 成功した接続を返す
}
lastErr = racer.error
nracers--
}
この変更により、コードはより直接的で読みやすくなりました。lane
チャネルからの受信が、select
という余分な構文なしに明示的に行われるため、開発者はこの行が単にチャネルからの値の到着を待っていることをすぐに理解できます。機能的な動作は変更されていませんが、コードの意図がより明確になり、保守性が向上しました。
コアとなるコードの変更箇所
--- a/src/pkg/net/dial.go
+++ b/src/pkg/net/dial.go
@@ -214,14 +214,12 @@ func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Con
nracers := len(ras)
for nracers > 0 {
sig <- true
- select {
- case racer := <-lane:
- if racer.error == nil {
- return racer.Conn, nil
- }
- lastErr = racer.error
- nracers--
- }
+ racer := <-lane
+ if racer.error == nil {
+ return racer.Conn, nil
+ }
+ lastErr = racer.error
+ nracers--
}
return nil, lastErr
}
コアとなるコードの解説
変更はsrc/pkg/net/dial.go
ファイルのdialMulti
関数内で行われています。
-
変更前:
select { case racer := <-lane: if racer.error == nil { return racer.Conn, nil } lastErr = racer.error nracers-- }
この部分では、
select
ステートメントが使用されており、lane
チャネルからの受信(case racer := <-lane:
)のみを待機しています。lane
チャネルはnilではないため、このselect
は常にlane
チャネルからの値が利用可能になるまでブロックし、その値を受信します。 -
変更後:
racer := <-lane if racer.error == nil { return racer.Conn, nil } lastErr = racer.error nracers--
変更後では、冗長な
select
ステートメントが完全に削除され、racer := <-lane
という直接的なチャネル受信操作に置き換えられています。これにより、コードはより簡潔になり、lane
チャネルからの値の受信が直接的に表現されるようになりました。その後のエラーチェックとカウンタのデクリメントのロジックは変更されていません。
この変更は、機能的な動作を変えることなく、コードの明瞭さと簡潔さを向上させるためのリファクタリングです。
関連リンク
- Go CL 103920044: https://golang.org/cl/103920044
参考にした情報源リンク
- Go言語の公式ドキュメント:
- Go言語のチャネルに関する一般的な情報源。
- Goの
net
パッケージのソースコード(src/pkg/net/dial.go
)。