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

[インデックス 11505] ファイルの概要

このコミットは、Go言語の標準ライブラリであるnetパッケージにおけるエラー変数の命名規則を統一するためのリファクタリングです。具体的には、エラーを示す変数名として使用されていたeerrnoを、よりGo言語の慣習に沿ったerrに置き換える変更が広範囲にわたって適用されています。

コミット

commit 28397befabea2bb984b8eda963d9e7c16ffafd45
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Wed Feb 1 00:36:45 2012 +0900

    net: replace error variable name e, errno with err
    
    R=rsc, r
    CC=golang-dev
    https://golang.org/cl/5593046

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/28397befabea2bb984b8eda963d9e7c16ffafd45

元コミット内容

net: replace error variable name e, errno with err

このコミットの目的は、netパッケージ内で使用されているエラー変数の名前をeerrnoからerrに統一することです。

変更の背景

Go言語では、関数がエラーを返す場合、慣習的に最後の戻り値としてerror型を返し、その変数名にはerrを使用することが推奨されています。これはGoコミュニティ全体で広く受け入れられているスタイルガイドの一部であり、コードの可読性と一貫性を高めることを目的としています。

このコミットが行われた2012年2月は、Go言語がまだ比較的新しい時期であり、標準ライブラリ内でもコーディングスタイルや命名規則の統一が進められていた段階でした。netパッケージはネットワーク関連の非常に重要な機能を提供する部分であり、そのコードベース全体で一貫したエラーハンドリングの慣習を適用することは、将来的なメンテナンス性や新規開発者のオンボーディングにおいて非常に重要です。

以前はeerrnoといった短い変数名が使われていましたが、これらは一般的なエラー変数名であるerrと比較して、コードを読んだ際に一瞬の判断を要する可能性があります。特に、errnoはC言語のシステムコールでよく使われるエラー番号を指すため、Goのerrorインターフェースとは異なるニュアンスを持つ可能性があり、混乱を招くことも考えられます。このリファクタリングは、このような潜在的な混乱を排除し、Goらしいエラーハンドリングのスタイルを徹底するためのものです。

前提知識の解説

Go言語のエラーハンドリング

Go言語には例外処理の仕組み(try-catchなど)は存在せず、エラーは関数の戻り値として明示的に扱われます。慣習として、エラーを返す可能性のある関数は、最後の戻り値として組み込みのerrorインターフェース型の値を返します。エラーがない場合はnilを返します。

func doSomething() (result string, err error) {
    // ... 処理 ...
    if someCondition {
        return "", errors.New("something went wrong") // エラーを返す
    }
    return "success", nil // 成功を返す
}

この際、エラーを受け取る変数名には通常errが使用されます。

result, err := doSomething()
if err != nil {
    // エラー処理
    log.Printf("Error: %v", err)
    return
}
// 成功時の処理

syscallパッケージとerrno

Go言語のsyscallパッケージは、オペレーティングシステムの低レベルなシステムコールへのアクセスを提供します。C言語のシステムコールでは、エラーが発生した場合にグローバル変数errnoにエラーコードが設定されることが一般的です。Goのsyscallパッケージも、これらのシステムコールをラップする際に、C言語のerrnoに対応するエラー値を返すことがあります。

このコミット以前のコードでは、syscallパッケージから返されるエラーをerrnoという変数名で受け取っている箇所が見られました。しかし、Goのエラーハンドリングの慣習では、syscallパッケージから返されるエラーも最終的にはerrorインターフェースとして扱われるため、errnoという変数名はGoのエラーハンドリングの文脈では不自然に映る可能性があります。errに統一することで、Goのエラーインターフェースとして一貫して扱われることが明確になります。

netFD構造体とネットワークI/O

Goのnetパッケージは、ネットワーク通信の基盤を提供します。その内部では、ソケットディスクリプタ(ファイルディスクリプタ)を管理するためのnetFDという内部構造体が頻繁に登場します。このnetFDは、ネットワーク接続の読み書き、接続、受け入れ(accept)などの低レベルな操作をカプセル化しています。

このコミットで変更されているファイルの多くは、netFDに関連するメソッドや、特定のOS(Darwin, FreeBSD, Linux, Windowsなど)に特化したネットワークI/O処理を扱うファイルです。これらのファイルでは、システムコールを直接呼び出す箇所が多く、エラーハンドリングが頻繁に行われます。

技術的詳細

このコミットの技術的な変更は、主に以下の2点に集約されます。

  1. エラー変数名の統一:

    • eという短い変数名でエラーを受け取っていた箇所をerrに変更。
    • errnoという変数名でエラーを受け取っていた箇所をerrに変更。
    • これにより、コード全体でエラー変数がerrとして一貫して扱われるようになります。
  2. 関数シグネチャの明示的なエラー戻り値:

    • 以前はfunc someFunc() (f *Type, err error)のように、戻り値の変数名が宣言されている場合、return nil, eのように変数名を省略してeを返しても、Goコンパイラは自動的にeerrに割り当てていました(名前付き戻り値の機能)。
    • しかし、このコミットでは、return nil, eのような箇所をreturn nil, errと明示的に変更しています。これは、変数名の変更に伴う自然な流れですが、より明示的なコード記述を促進する側面もあります。
    • また、一部の関数では、戻り値の型宣言がfunc someFunc() (err error)のように変数名を含んでいたものを、func someFunc() errorのように変数名を省略する変更も含まれています。これは、戻り値が単一のエラーである場合に、より簡潔な記述を好むGoのスタイルに合わせたものです。

これらの変更は、Goのgofmtのような自動フォーマッタでは検出・修正されない種類の変更であり、開発者が手動でコードベース全体をレビューし、Goの慣習に沿って修正する必要があります。このコミットは、Go標準ライブラリの品質と一貫性を高めるための、地道ながらも重要な作業の一環と言えます。

コアとなるコードの変更箇所

変更は多岐にわたりますが、src/pkg/net/fd.gonewFD関数における変更が典型的な例です。

変更前:

func newFD(fd, family, sotype int, net string) (f *netFD, err error) {
	onceStartServer.Do(startServer)
	if e := syscall.SetNonblock(fd, true); e != nil {
		return nil, e
	}
	f = &netFD{
		sysfd:  fd,
		family: family,
		sotype: sotype,
		net:    net,
	}
	f.cr = make(chan bool, 1)
	f.cw = make(chan bool, 1)
	return f, nil
}

変更後:

func newFD(fd, family, sotype int, net string) (*netFD, error) {
	onceStartServer.Do(startServer)
	if err := syscall.SetNonblock(fd, true); err != nil {
		return nil, err
	}
	netfd := &netFD{
		sysfd:  fd,
		family: family,
		sotype: sotype,
		net:    net,
	}
	netfd.cr = make(chan bool, 1)
	netfd.cw = make(chan bool, 1)
	return netfd, nil
}

コアとなるコードの解説

上記のnewFD関数の変更は、このコミットの意図を明確に示しています。

  1. if e := syscall.SetNonblock(fd, true); e != nil { return nil, e }

    • ここでsyscall.SetNonblockが返すエラーを変数eに代入し、そのenilでない場合にエラー処理を行っています。
    • 変更後には、このeerrに置き換えられています。
    • if err := ...; err != nilというパターンは、Go言語でエラーハンドリングを行う際の最も一般的で推奨されるイディオムです。
  2. func newFD(...) (f *netFD, err error) から func newFD(...) (*netFD, error) への変更

    • 変更前は、戻り値の変数名ferrが明示的に宣言されていました(名前付き戻り値)。
    • 変更後では、戻り値の型のみが指定され、変数名は省略されています。これは、関数内でnetfdという新しいローカル変数を宣言し、それを明示的にreturn netfd, nilとして返すスタイルに合わせたものです。Goでは、戻り値の変数名が自明である場合や、関数が短い場合に、このように変数名を省略することがよくあります。

この変更は、機能的な振る舞いを一切変えることなく、コードのスタイルと一貫性を向上させる純粋なリファクタリングです。これにより、Go言語の標準ライブラリがよりGoらしい慣習に沿ったものとなり、開発者にとって読みやすく、理解しやすいコードベースが維持されます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメントおよびGo Wiki
  • Go言語のソースコード(特にnetパッケージ)
  • Goコミュニティにおける一般的なコーディング慣習とスタイルガイド
  • Gitのコミット履歴と差分表示
  • Go言語におけるエラーハンドリングに関する一般的な知識
  • syscallパッケージの動作に関する知識
  • ネットワークプログラミングの基礎知識