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

[インデックス 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.gounixsock_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のように別のエラー変数を宣言し、それを使用している箇所がありました。このような場合、oserrerrとは別の変数として扱われます。

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 が返される
}

このコードの問題点は以下の通りです。

  1. 命名規則の不統一: Goの慣習に反してoserrという変数名が使われている。
  2. 冗長性: 既にerrという戻り値変数が存在しているにもかかわらず、別のエラー変数を導入している。
  3. 潜在的な混乱: erroserrという2つのエラー変数が存在することで、どちらが最終的なエラーを表すのか、あるいはどこでどちらの変数が使われているのかが読みにくくなる可能性がある。特に、goto Errorラベルにジャンプした際に、oserrの値が最終的に返されるOpErrorErrフィールドに設定されるため、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 {

コアとなるコードの解説

両ファイルにおける変更は本質的に同じです。

  1. var oserr error の削除: internetSocket関数とunixSocket関数内でローカル変数oserrが宣言されていましたが、これが削除されました。これは、関数の戻り値として既にerr errorが名前付き戻り値として宣言されており、その変数を再利用できるため、oserrが不要になったためです。

  2. oserrerr に置き換え:

    • 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言語の標準的なエラーハンドリングパターンに完全に準拠し、よりクリーンで理解しやすいものになりました。

関連リンク

参考にした情報源リンク