[インデックス 12139] ファイルの概要
このコミットは、Go言語の標準ライブラリであるnet
パッケージ内のipsock_posix.go
およびunixsock_posix.go
ファイルにおいて、エラー変数の命名規則を統一する変更を行っています。具体的には、ローカル変数として宣言されていたoserr
という名前のエラー変数を、関数の戻り値として定義されているerr
変数に置き換えることで、コードベース全体での一貫性を高めています。
コミット
- コミットハッシュ:
215777b332b9ccf167ef90e2bdd3d241021eb791
- Author: Mikio Hara mikioh.mikioh@gmail.com
- Date: Wed Feb 22 19:08:19 2012 +0900
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/215777b332b9ccf167ef90e2bdd3d241021eb791
元コミット内容
net: replace error variable name oserr with err
This CL replaces the last two in source tree.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5685080
変更の背景
Go言語のコードベースでは、エラーを扱う際の変数名として慣習的にerr
が使用されます。これは、Goのエラーハンドリングの基本的なパターンであり、コードの可読性と一貫性を保つ上で非常に重要です。
このコミットが行われた2012年2月時点では、Go言語はまだ比較的新しい言語であり、標準ライブラリ内でも一部に古い命名規則や一貫性のないコードが残存していました。net
パッケージ内のipsock_posix.go
とunixsock_posix.go
ファイルには、エラーを格納するためにoserr
というローカル変数が使用されている箇所が残っていました。
この変更の背景には、Go言語のコードベース全体でエラー変数の命名規則をerr
に統一し、よりGoらしい(Idiomatic Go)コードスタイルを徹底するという目的があります。これにより、開発者がコードを読み書きする際の認知負荷を減らし、コードベース全体の品質と保守性を向上させることが期待されます。コミットメッセージにある「This CL replaces the last two in source tree.」という記述から、この変更がコードベース全体における同様の不統一を解消する一連の作業の最終段階であったことが伺えます。
前提知識の解説
Go言語のエラーハンドリング
Go言語では、エラーは多値戻り値の2番目の値として返されるのが一般的です。慣習的に、このエラー値はerror
インターフェース型であり、変数名にはerr
が使われます。
func doSomething() (resultType, error) {
// ... 処理 ...
if someCondition {
return defaultValue, fmt.Errorf("something went wrong: %w", someError)
}
return actualResult, nil
}
呼び出し側では、通常以下のようにif err != nil
でエラーの有無をチェックします。
result, err := doSomething()
if err != nil {
// エラー処理
log.Printf("Error: %v", err)
return
}
// 正常処理
変数のスコープとシャドーイング
Go言語では、変数のスコープはブロック({}
で囲まれた範囲)によって決まります。関数内で同じ名前の変数を宣言すると、内側のスコープで宣言された変数が外側のスコープの変数を「シャドーイング」します。
このコミットでは、関数の戻り値としてerr error
が既に宣言されているにもかかわらず、関数内部でvar oserr error
のように別のエラー変数を宣言し、それを使用している箇所がありました。このような場合、oserr
はerr
とは別の変数として扱われます。
net
パッケージとソケットプログラミング
net
パッケージは、Go言語でネットワークプログラミングを行うための基本的な機能を提供します。TCP/IPソケット、UDPソケット、Unixドメインソケットなど、様々なネットワーク通信を扱うことができます。
syscall.Sockaddr
: オペレーティングシステムがソケットアドレスを表現するために使用するインターフェースです。Goのnet
パッケージは、低レベルのシステムコールをラップして、より抽象化されたインターフェースを提供しています。netFD
:net
パッケージ内部でファイルディスクリプタ(ファイル記述子)を管理するための構造体です。ソケット操作の基盤となります。OpError
:net
パッケージで発生したネットワーク操作のエラーをラップするためのカスタムエラー型です。操作の種類(Op
)、ネットワークの種類(Net
)、アドレス(Addr
)、そして元のエラー(Err
)を含みます。
技術的詳細
このコミットの技術的な詳細は、主にGo言語の変数命名規則の統一と、それによるコードの保守性向上にあります。
変更前は、internetSocket
関数やunixSocket
関数において、関数の戻り値としてerr error
が宣言されているにもかかわらず、関数内部で一時的なエラーを捕捉するためにoserr
というローカル変数が宣言されていました。
// 変更前 (ipsock_posix.go の一部)
func internetSocket(...) (fd *netFD, err error) { // err が戻り値として宣言されている
var oserr error // ここで別の oserr が宣言されている
// ...
if la, oserr = laddr.sockaddr(family); oserr != nil { // oserr にエラーが代入される
goto Error
}
// ...
fd, oserr = socket(net, family, sotype, proto, la, ra, toAddr) // oserr にエラーが代入される
if oserr != nil {
goto Error
}
// ...
Error:
// ...
return nil, &OpError{mode, net, addr, oserr} // 最終的に oserr が返される
}
このコードの問題点は以下の通りです。
- 命名規則の不統一: Goの慣習に反して
oserr
という変数名が使われている。 - 冗長性: 既に
err
という戻り値変数が存在しているにもかかわらず、別のエラー変数を導入している。 - 潜在的な混乱:
err
とoserr
という2つのエラー変数が存在することで、どちらが最終的なエラーを表すのか、あるいはどこでどちらの変数が使われているのかが読みにくくなる可能性がある。特に、goto Error
ラベルにジャンプした際に、oserr
の値が最終的に返されるOpError
のErr
フィールドに設定されるため、err
戻り値変数は未使用のままになるか、意図しない値を持つ可能性があります。
このコミットでは、oserr
変数の宣言を削除し、その代わりに既存の戻り値変数err
を直接使用するように変更しています。
// 変更後 (ipsock_posix.go の一部)
func internetSocket(...) (fd *netFD, err error) { // err が戻り値として宣言されている
var la, ra syscall.Sockaddr // oserr の宣言が削除された
// ...
if la, err = laddr.sockaddr(family); err != nil { // err にエラーが代入される
goto Error
}
// ...
fd, err = socket(net, family, sotype, proto, la, ra, toAddr) // err にエラーが代入される
if err != nil {
goto Error
}
// ...
Error:
// ...
return nil, &OpError{mode, net, addr, err} // err が返される
}
この変更により、以下の利点が得られます。
- コードの一貫性: Goのエラーハンドリングの慣習に従い、
err
という変数名に統一されます。 - 可読性の向上: エラー変数が一つに絞られるため、コードの流れが追いやすくなります。
- 保守性の向上: 将来的にコードを修正する際に、エラーの扱いに関する混乱が少なくなります。
- 冗長性の排除: 不要なローカル変数の宣言がなくなるため、コードがより簡潔になります。
Go言語では、関数の戻り値として名前付き戻り値(fd *netFD, err error
のように戻り値に変数を指定する)を使用した場合、その変数は関数スコープ内で自動的に宣言され、ゼロ値(error
型の場合はnil
)で初期化されます。関数内でその変数に値を代入すると、それが最終的な戻り値となります。このコミットは、このGoの言語機能を最大限に活用し、よりクリーンなコードを実現しています。
コアとなるコードの変更箇所
src/pkg/net/ipsock_posix.go
--- a/src/pkg/net/ipsock_posix.go
+++ b/src/pkg/net/ipsock_posix.go
@@ -105,21 +105,20 @@ type sockaddr interface {
}
func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
- var oserr error
var la, ra syscall.Sockaddr
family := favoriteAddrFamily(net, laddr, raddr, mode)
if laddr != nil {
- if la, oserr = laddr.sockaddr(family); oserr != nil {
+ if la, err = laddr.sockaddr(family); err != nil {
goto Error
}
}
if raddr != nil {
- if ra, oserr = raddr.sockaddr(family); oserr != nil {
+ if ra, err = raddr.sockaddr(family); err != nil {
goto Error
}
}
- fd, oserr = socket(net, family, sotype, proto, la, ra, toAddr)
- if oserr != nil {
+ fd, err = socket(net, family, sotype, proto, la, ra, toAddr)
+ if err != nil {
goto Error
}
return fd, nil
@@ -129,7 +128,7 @@ Error:
if mode == "listen" {
addr = laddr
}
- return nil, &OpError{mode, net, addr, oserr}
+ return nil, &OpError{mode, net, addr, err}
}
func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) {
src/pkg/net/unixsock_posix.go
--- a/src/pkg/net/unixsock_posix.go
+++ b/src/pkg/net/unixsock_posix.go
@@ -59,8 +59,8 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err
f = sockaddrToUnixpacket
}
- fd, oserr := socket(net, syscall.AF_UNIX, sotype, 0, la, ra, f)
- if oserr != nil {
+ fd, err = socket(net, syscall.AF_UNIX, sotype, 0, la, ra, f)
+ if err != nil {
goto Error
}
return fd, nil
@@ -70,7 +70,7 @@ Error:
if mode == "listen" {
addr = laddr
}
- return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: oserr}
+ return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: err}
}
func sockaddrToUnix(sa syscall.Sockaddr) Addr {
コアとなるコードの解説
両ファイルにおける変更は本質的に同じです。
-
var oserr error
の削除:internetSocket
関数とunixSocket
関数内でローカル変数oserr
が宣言されていましたが、これが削除されました。これは、関数の戻り値として既にerr error
が名前付き戻り値として宣言されており、その変数を再利用できるため、oserr
が不要になったためです。 -
oserr
をerr
に置き換え:laddr.sockaddr(family)
からの戻り値を受け取る部分:if la, oserr = laddr.sockaddr(family); oserr != nil {
がif la, err = laddr.sockaddr(family); err != nil {
に変更されました。これにより、sockaddr
メソッドが返すエラーが直接err
戻り値変数に代入されるようになりました。socket(...)
からの戻り値を受け取る部分:fd, oserr = socket(...)
がfd, err = socket(...)
に変更されました。同様に、socket
関数が返すエラーも直接err
戻り値変数に代入されます。- エラーハンドリングの
goto Error
ラベルの後のreturn
文:return nil, &OpError{..., oserr}
がreturn nil, &OpError{..., err}
に変更されました。これにより、最終的にOpError
構造体のErr
フィールドに設定されるエラーも、統一されたerr
変数からの値となります。
これらの変更により、コードはGo言語の標準的なエラーハンドリングパターンに完全に準拠し、よりクリーンで理解しやすいものになりました。
関連リンク
- Go CL 5685080: https://golang.org/cl/5685080
参考にした情報源リンク
- Go言語公式ドキュメント: https://go.dev/doc/
- Effective Go - Error Handling: https://go.dev/doc/effective_go#errors
- Go Code Review Comments - Error Handling: https://go.dev/wiki/CodeReviewComments#error-handling
- Go言語のnetパッケージに関するドキュメント (当時のバージョンに基づく): https://pkg.go.dev/net (現在の最新版)
- Go言語のsyscallパッケージに関するドキュメント (当時のバージョンに基づく): https://pkg.go.dev/syscall (現在の最新版)
- Go言語の命名規則に関する一般的なガイドライン: https://go.dev/doc/effective_go#names
- Go言語の
goto
文に関する情報: https://go.dev/ref/spec#Goto_statements - Go言語の名前付き戻り値に関する情報: https://go.dev/ref/spec#Return_statements
- Go言語の変数宣言とスコープに関する情報: https://go.dev/ref/spec#Declarations_and_scope
- Go言語の
OpError
構造体に関する情報: https://pkg.go.dev/net#OpError