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

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

このコミットは、Go言語の標準ライブラリnetパッケージ内のinterface_bsd.goファイルに対する変更です。具体的には、変数宣言の位置を調整し、コードの可読性とGoのイディオムに沿った記述に改善しています。

コミット

commit c2331bb77e4b2ef864c2e14a248952eb8b74a3fd
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Fri Feb 3 07:40:03 2012 +0900

    net: tweak variable declarations

    R=golang-dev, bradfitz, rsc
    CC=golang-dev
    https://golang.org/cl/5617050

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

https://github.com/golang/go/commit/c2331bb77e4b2ef864c2e14a248952eb8b74a3fd

元コミット内容

net: tweak variable declarations

このコミットは、netパッケージ内の変数宣言を調整するものです。

変更の背景

Go言語では、変数をその変数が初めて使用される場所にできるだけ近くで宣言することが推奨されるイディオムの一つです。これはコードの可読性を高め、変数のスコープを最小限に抑えることで、コードの理解と保守を容易にするためです。

このコミットが行われた2012年2月時点では、Go言語はまだ比較的新しく、言語のイディオムやベストプラクティスが確立されつつある段階でした。この変更は、既存のコードベースをGoの推奨するスタイルに合わせるための、継続的なリファクタリングの一環と考えられます。特に、syscallパッケージからの戻り値を受け取る前に変数を宣言するのではなく、syscall呼び出しが成功し、変数が実際に必要になる直前に宣言することで、コードの流れがより自然になります。

前提知識の解説

  • Go言語の変数宣言: Go言語では、varキーワードを用いた明示的な宣言や、:=演算子を用いた短い変数宣言(型推論を伴う)が可能です。Goのイディオムとして、変数はその変数が初めて使われる場所の直前で宣言することが推奨されます。これにより、変数のライフサイクルが明確になり、コードの理解が容易になります。
  • netパッケージ: Go言語の標準ライブラリの一部で、ネットワークI/O機能を提供します。TCP/IP、UDP、DNSルックアップなど、様々なネットワークプロトコルを扱うためのインターフェースが含まれています。
  • syscallパッケージ: オペレーティングシステムが提供する低レベルなシステムコールへのインターフェースを提供します。このコミットで変更されているinterface_bsd.goは、BSD系のOS(FreeBSD, OpenBSD, macOSなど)におけるネットワークインターフェース情報の取得に、syscallパッケージを利用しています。
  • syscall.RouteRIB: ルーティング情報ベース (RIB: Routing Information Base) から情報を取得するためのシステムコールです。ネットワークインターフェースの情報(IPアドレス、MACアドレスなど)を取得するために使用されます。
  • syscall.ParseRoutingSockaddr: ルーティングメッセージからソケットアドレス構造体をパースするための関数です。ネットワークインターフェースに関連するアドレス情報を抽出するために使用されます。

技術的詳細

このコミットの技術的な変更は非常にシンプルで、主にコードのスタイルと可読性に関するものです。

変更前は、関数内で使用するスライスやポインタ変数が、関数の冒頭でまとめて宣言されていました。例えば、interfaceTable関数では、syscall.RouteRIBの呼び出しよりも前にvar ift []Interfaceが宣言されていました。

変更後は、これらの変数宣言が、実際にその変数が初期化されるか、または初めて使用される直前に移動されています。

例: interfaceTable関数におけるift変数の宣言

変更前:

func interfaceTable(ifindex int) ([]Interface, error) {
	var ift []Interface // 関数の冒頭で宣言

	tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex)
	// ...
}

変更後:

func interfaceTable(ifindex int) ([]Interface, error) {
	tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex)
	// ...
	var ift []Interface // syscall.RouteRIBの呼び出し後に移動
	for _, m := range msgs {
	// ...
}

この変更は、以下の点でコードの品質を向上させます。

  1. 可読性の向上: 変数が宣言された直後に使用されるため、コードを上から下に読み進める際に、変数の目的と初期値がすぐに理解できます。
  2. スコープの最小化: 変数のスコープが不必要に広がることを防ぎます。これにより、変数が意図しない場所で変更されたり、誤って使用されたりするリスクが減少します。
  3. Goのイディオムへの準拠: Goコミュニティで推奨されるコーディングスタイルに合致します。これにより、他のGo開発者がコードを読み解く際の認知負荷が軽減されます。

この変更は機能的な影響は一切なく、プログラムの動作に変化はありません。純粋にコードの保守性と可読性を高めるためのリファクタリングです。

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

変更はsrc/pkg/net/interface_bsd.goファイル内の以下の4つの関数で行われています。

  1. func interfaceTable(ifindex int) ([]Interface, error)
  2. func newLink(m *syscall.InterfaceMessage) ([]Interface, error)
  3. func interfaceAddrTable(ifindex int) ([]Addr, error)
  4. func newAddr(m *syscall.InterfaceAddrMessage) (Addr, error)

それぞれの関数で、スライスやポインタの変数宣言が、その変数が実際に使用される直前に移動されています。

コアとなるコードの解説

以下に、interfaceTable関数を例にとり、変更前後のコードを比較して解説します。

変更前 (src/pkg/net/interface_bsd.go):

func interfaceTable(ifindex int) ([]Interface, error) {
	var ift []Interface // ここで宣言

	tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex)
	if err != nil {
		return nil, os.NewSyscallError("route rib", err)
	}
	msgs, err := syscall.ParseRoutingMessage(tab)
	if err != nil {
		return nil, os.NewSyscallError("route message", err)
	}

	for _, m := range msgs {
		switch v := m.(type) {
		case *syscall.InterfaceMessage:
			ifis, err := newLink(v)
			if err != nil {
				return nil, err
			}
			for _, ifi := range ifis {
				ift = append(ift, ifi) // ここで初めて使用
			}
		}
	}
	return ift, nil
}

変更後 (src/pkg/net/interface_bsd.go):

func interfaceTable(ifindex int) ([]Interface, error) {
	tab, err := syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex)
	if err != nil {
		return nil, os.NewSyscallError("route rib", err)
	}
	msgs, err := syscall.ParseRoutingMessage(tab)
	if err != nil {
		return nil, os.NewSyscallError("route message", err)
	}

	var ift []Interface // ここに移動
	for _, m := range msgs {
		switch v := m.(type) {
		case *syscall.InterfaceMessage:
			ifis, err := newLink(v)
			if err != nil {
				return nil, err
			}
			for _, ifi := range ifis {
				ift = append(ift, ifi)
			}
		}
	}
	return ift, nil
}

この変更により、iftスライスがsyscall.ParseRoutingMessageの呼び出しが成功し、msgsが利用可能になった直後に宣言されるようになりました。これにより、iftが実際にデータが追加されるループの直前で宣言されることになり、コードの意図がより明確になります。

同様の変更が、newLink関数内のiftinterfaceAddrTable関数内のifat、そしてnewAddr関数内のifaに対しても適用されています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメントおよびパッケージドキュメント
  • Go言語のコーディングスタイルに関する一般的な知識
  • コミットメッセージと差分情報
  • Go言語のnetパッケージとsyscallパッケージのソースコード
  • Go言語のChange List (CL) 5617050: https://golang.org/cl/5617050 (これはコミットメッセージに記載されているリンクであり、このコミットの直接の変更内容を示しています。)