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

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

このコミットは、Go言語の標準ライブラリ、特にnetパッケージとosパッケージにおける広範なクリーンアップとリファクタリングを目的としています。主な変更点は、パッケージ内部で利用されるプライベートな(エクスポートされない)識別子に付与されていたアンダースコア(_)プレフィックスの削除、os_test.goにおけるファイル名リストの修正、そしてErrorf関数の改行に関する調整です。これらの変更は、Go言語の初期段階におけるコーディング規約の進化と、よりクリーンで慣用的なコードベースへの移行を示しています。

コミット

commit d8921c5294cd0698184dc05b2af949ae1b147d12
Author: Russ Cox <rsc@golang.org>
Date:   Sun Feb 15 14:18:39 2009 -0800

    cleanups:
            get rid of _ on private names in net.
            fix os_test file name list.
            newline not needed on Errorf.
    
    R=r
    DELTA=305  (34 added, 2 deleted, 269 changed)
    OCL=25047
    CL=25047

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

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

元コミット内容

cleanups:
        get rid of _ on private names in net.
        fix os_test file name list.
        newline not needed on Errorf.

R=r
DELTA=305  (34 added, 2 deleted, 269 changed)
OCL=25047
CL=25047

変更の背景

このコミットが行われた2009年2月は、Go言語がまだ一般に公開される前の非常に初期の段階でした。当時のGo言語のコーディング規約や言語仕様はまだ流動的であり、開発者たちは試行錯誤を繰り返していました。

このコミットの主な背景は以下の点に集約されます。

  1. Go言語の命名規約の確立: Go言語では、識別子(変数名、関数名、型名など)の最初の文字が大文字か小文字かによって、その識別子がパッケージ外にエクスポートされる(公開される)か、パッケージ内部でのみ利用される(プライベート)かが決定されます。初期のGo言語開発では、プライベートな識別子にアンダースコア(_)をプレフィックスとして付ける慣習が見られました。これは、他の言語(例: Python)におけるプライベートなメンバーを示す慣習に影響を受けた可能性があります。しかし、Go言語の設計思想として、シンプルさと明示性を重視する方向性が強まるにつれて、このアンダースコアは冗長であると判断され、削除されることになりました。このコミットは、その移行期における大規模なリファクタリングの一環です。
  2. コードベースの統一性と保守性の向上: アンダースコアプレフィックスの削除は、コードの見た目をすっきりさせ、Goの命名規約に統一性をもたらします。これにより、コードの可読性が向上し、将来的な保守が容易になります。
  3. osパッケージのファイル名変更への対応: osパッケージ内のファイル名が以前に変更されたこと(例: os_env.goからenv.goへ)に伴い、テストファイルos_test.go内の参照リストを更新する必要がありました。これは、コードベース全体の整合性を保つための修正です。
  4. Errorfの挙動の明確化: コミットメッセージにある「newline not needed on Errorf」は、エラーメッセージのフォーマットに関する細かな調整を示唆しています。これは、fmt.Errorfのような関数が自動的に改行を追加するようになったか、あるいは開発者が明示的に改行を追加する必要がなくなったことを意味し、エラーロギングの一貫性を高めるための改善と考えられます。

これらの変更は、Go言語がより成熟した、一貫性のある、そして「Goらしい」コードベースへと進化していく過程の重要なステップでした。

前提知識の解説

このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念と、当時の開発環境に関する知識が役立ちます。

  1. Go言語の命名規約(エクスポートとアンエクスポート):

    • Go言語では、識別子(関数名、変数名、型名、メソッド名など)の最初の文字が大文字で始まる場合、その識別子はパッケージ外から参照可能(エクスポートされる)になります。これは、他の言語におけるpublicキーワードに相当します。
    • 一方、識別子の最初の文字が小文字で始まる場合、その識別子はパッケージ内部でのみ参照可能(エクスポートされない、またはプライベート)になります。これは、他の言語におけるprivateキーワードに相当します。
    • このシンプルかつ強力な規約により、Go言語では明示的なアクセス修飾子を記述する必要がありません。
    • このコミット以前は、プライベートな識別子に_プレフィックスを付ける慣習がありましたが、このコミットによってその慣習が廃止され、小文字で始まる識別子のみがプライベートであることを示す標準的な方法となりました。
  2. netパッケージ:

    • Go言語の標準ライブラリの一部であり、ネットワークI/O機能を提供します。TCP/UDP接続、DNSルックアップ、IPアドレスの操作など、ネットワークプログラミングの基盤となる機能が含まれています。
    • このコミットでは、netパッケージ内の多くの内部関数や型がリファクタリングされています。
  3. osパッケージ:

    • Go言語の標準ライブラリの一部であり、オペレーティングシステムとのインタラクション機能を提供します。ファイルシステム操作、プロセス管理、環境変数へのアクセスなどが含まれます。
    • このコミットでは、osパッケージのテストファイルが修正されています。
  4. syscallパッケージ:

    • Go言語の標準ライブラリの一部であり、低レベルのシステムコールへのアクセスを提供します。ネットワークソケットの操作、ファイルディスクリプタの管理など、OS固有の機能に直接アクセスするために使用されます。
    • netパッケージは、内部的にsyscallパッケージを利用してネットワーク操作を行っています。
  5. reflectパッケージ:

    • Go言語の標準ライブラリの一部であり、実行時のリフレクション機能を提供します。これにより、プログラムは自身の構造(型、フィールド、メソッドなど)を検査し、動的に操作することができます。
    • net/dnsmsg.goでは、DNSメッセージのパッキング/アンパッキングにリフレクションが使用されており、このコミットでもその関連コードが修正されています。
  6. once.Do:

    • syncパッケージ(Go 1.0以降)または初期のGo言語の同等のメカニズムで提供される機能で、指定された関数がプログラムの実行中に一度だけ実行されることを保証します。これは、初期化処理(例: 設定ファイルの読み込み、シングルトンの生成)に非常に有用です。
    • このコミットでは、netパッケージ内でDNS設定の読み込みやポーリングサーバーの起動にonce.Doが使用されています。
  7. Go言語の初期コンパイラ(6gなど):

    • Go言語の初期には、6g(x86-64アーキテクチャ向け)、8g(x86向け)、5g(ARM向け)といったコンパイラが使用されていました。これらは現在のgo buildコマンドの背後にあるコンパイラとは異なり、より実験的な段階にありました。
    • コミット内のコメント「// TODO(rsc): 6g won't let me use "file :="」は、当時の6gコンパイラが特定の構文(ここでは、:=による変数宣言と同時にos.Openのような関数呼び出しの結果を代入する)を許可していなかったという、初期のGo言語のコンパイラの制約を示しています。これは、Go言語の進化の歴史を垣間見せる興味深い点です。

これらの知識を持つことで、単なるコード変更以上の、Go言語の設計思想と進化の過程を深く理解することができます。

技術的詳細

このコミットは、Go言語のnetパッケージとosパッケージにおける複数の技術的な側面を改善しています。

  1. プライベート識別子の命名規約の統一:

    • 最も顕著な変更は、netパッケージ内の多くの関数、型、変数からアンダースコア(_)プレフィックスが削除されたことです。例えば、_Exchangeexchangeに、_PollServerpollServerに、_FDnetFDに変更されています。
    • Go言語では、識別子の最初の文字が小文字であればその識別子はパッケージプライベートであり、アンダースコアは不要です。この変更は、Goの命名規約をより厳密に適用し、コードの冗長性を排除することを目的としています。
    • このリファクタリングは、dnsclient.go, dnsconfig.go, dnsmsg.go, fd.go, ip.go, net.go, net_linux.go, parse.go, port.goといったnetパッケージのほぼ全てのファイルにわたって行われています。これにより、netパッケージ全体のコードベースがより一貫性のある、Goらしいスタイルに統一されました。
  2. netFD型の強化:

    • src/lib/net/fd.goでは、ネットワークファイルディスクリプタを表すFD型がnetFDにリネームされました。
    • さらに、netFD型にはnet (ネットワークタイプ、例: "tcp", "udp")、laddr (ローカルアドレス)、raddr (リモートアドレス) という新しいフィールドが追加されています。
    • これらのフィールドは、os.NewFD関数に渡されるos.FDname引数にも反映され、デバッグやロギングの際にファイルディスクリプタがどのネットワーク接続に関連しているかをより明確に識別できるようになります。
    • crcwチャネルの型も*FDから*netFDに変更され、型安全性が向上しています。
    • Acceptメソッドでは、受け入れられた新しいソケットに対してsockaddrToHostPort関数を使用してリモートアドレスを解決し、newFD関数に渡すことで、新しいnetFDインスタンスがより豊富な接続情報を持つようにしています。
  3. 初期Goコンパイラの制約への対応:

    • src/lib/net/dnsconfig.gosrc/lib/net/port.goに見られるfile := _Open(...)からvar file = open(...)への変更と、それに付随するコメント「// TODO(rsc): 6g won't let me use "file :="」は、当時のGoコンパイラ(6g)の特定の制約を示しています。
    • Go言語の:=演算子(ショート変数宣言)は、変数の宣言と初期化を同時に行いますが、初期のコンパイラでは、特定のコンテキスト(特に、関数呼び出しの結果を複数の変数に代入する際など)でこの構文に制限があった可能性があります。このため、明示的にvarキーワードを使用して変数を宣言し、その後に値を代入する形式に変更されています。これは、Go言語のコンパイラがまだ開発初期段階であり、言語仕様が固まっていく過程で発生した一時的な回避策を示しています。
  4. osパッケージのテストファイル名の同期:

    • src/lib/os/os_test.goでは、テスト対象となるosパッケージ内のファイル名リストが更新されています。例えば、os_env.goenv.goに、os_error.goerror.goに、os_file.gofile.goに、os_time.gotime.goに、os_types.gotypes.goにそれぞれ変更されています。
    • これは、osパッケージ内の実際のファイル名変更にテストコードが追従したことを意味し、テストの正確性とコードベースの整合性を維持するために重要です。
  5. Errorfの改行に関する調整:

    • コミットメッセージに「newline not needed on Errorf」とありますが、提供されたdiffにはErrorfの直接的な変更は含まれていません。これは、fmtパッケージ(またはそれに相当する初期のパッケージ)のErrorf関数が、出力の最後に自動的に改行を追加するようになったか、あるいはそのように振る舞うことが期待されるようになったことを示唆している可能性があります。これにより、エラーメッセージのフォーマットが一貫し、開発者が手動で改行を追加する手間が省かれます。これは、Go言語の標準ライブラリがユーザーフレンドリーなAPIデザインを目指していたことの一例です。
  6. src/run.bashの更新:

    • Goプロジェクトのテスト実行スクリプトであるsrc/run.bashlib/osが追加されています。これは、osパッケージのテストがGoの標準テストスイートに含まれるようになったことを意味します。これにより、osパッケージの変更が継続的にテストされ、品質が保証されるようになります。

これらの技術的詳細は、Go言語がその初期段階でいかに活発に開発され、言語設計と実装の両面で洗練されていったかを示しています。特に、命名規約の統一は、Goのシンプルで一貫したデザイン哲学を象徴する変更と言えるでしょう。

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

このコミットにおけるコアとなるコードの変更箇所は、主に以下のファイルとパターンに集約されます。

  1. src/lib/net/*.go ファイル群におけるプライベート識別子のリネーム:

    • src/lib/net/dnsclient.go: _Answer -> answer, _TryOneName -> tryOneName, _LoadConfig -> loadConfig
    • src/lib/net/dnsconfig.go: _Open -> open, _GetFields -> getFields, _Dtoi -> dtoi, file.Close() -> file.close()
    • src/lib/net/dnsmsg.go: _Packet -> Packet, _Packing -> Packing, _PackDomainName -> packDomainName, _UnpackDomainName -> unpackDomainName, _PackStructValue -> packStructValue, _PackStruct -> packStruct, _UnpackStructValue -> unpackStructValue, _UnpackStruct -> unpackStruct, _PrintStructValue -> printStructValue, _PrintStruct -> printStruct, _PackRR -> packRR, _UnpackRR -> unpackRR
    • src/lib/net/fd.go: FD型 -> netFD型へのリネームと、関連するチャネル型(chan *FD -> chan *netFD)の変更。_SetNonblock -> setNonblock, _PollServer -> pollServer, _NewPollServer -> newPollServer, _StartServer -> startServer, NewFD -> newFD
    • src/lib/net/ip.go: _MakeIPv4 -> makeIPv4, _IsZeros -> isZeros, _SimpleMaskLength -> simpleMaskLength, _ParseIPv4 -> parseIPv4, _ParseIPv6 -> parseIPv6
    • src/lib/net/net.go: _SplitHostPort -> splitHostPort, _JoinHostPort -> joinHostPort, _HostPortToIP -> hostPortToIP, _SockaddrToHostPort -> sockaddrToHostPort, _Socket -> socket, _ConnBase -> connBase, _PreferIPv4 -> preferIPv4, _InternetSocket -> internetSocket, _NewConnTCP -> newConnTCP, _NewConnUDP -> newConnUDP
    • src/lib/net/net_linux.go: _IPv6zero -> ipv6zero
    • src/lib/net/parse.go: _File -> file, _File.Close -> file.close, _File.GetLineFromData -> file.getLineFromData, _File.ReadLine -> file.readLine, _Open -> open, _ByteIndex -> byteIndex, _CountAnyByte -> countAnyByte, _SplitAtBytes -> splitAtBytes, _GetFields -> getFields, _Big -> big, _Dtoi -> dtoi, _Xtoi -> xtoi
    • src/lib/net/port.go: _ReadServices -> readServices
  2. src/lib/os/os_test.goにおけるファイル名リストの修正:

    • dot変数内のファイル名が、os_env.goからenv.goのように、os_プレフィックスが削除された名前に変更されています。
  3. src/run.bashにおけるテスト対象の追加:

    • maketestコマンドの引数にlib/osが追加され、osパッケージのテストが実行されるように変更されています。

これらの変更は、Go言語の初期のコードベース全体にわたる大規模なリファクタリングであり、Goの命名規約の確立とコードベースの統一性向上に大きく貢献しています。

コアとなるコードの解説

このコミットのコアとなる変更は、Go言語の命名規約の進化と、それに伴う既存コードベースの大規模なリファクタリングです。

1. プライベート識別子からのアンダースコア削除

Go言語では、識別子の最初の文字が小文字であれば、その識別子はパッケージ内部でのみアクセス可能な「プライベート」なものとなります。初期のGo言語では、プライベートな識別子に_(アンダースコア)をプレフィックスとして付ける慣習がありましたが、これは冗長であると判断され、このコミットで一掃されました。

例えば、src/lib/net/dnsclient.goでは、DNSクエリの応答を解析する関数が_Answerからanswerにリネームされています。

--- a/src/lib/net/dnsclient.go
+++ b/src/lib/net/dnsclient.go
@@ -84,7 +84,7 @@ func _Exchange(cfg *DNS_Config, c Conn, name string) (m *DNS_Msg, err *os.Error)\
 // Find answer for name in dns message.
 // On return, if err == nil, addrs != nil.
 // TODO(rsc): Maybe return [][]byte (==[]IPAddr) instead?
-func _Answer(name string, dns *DNS_Msg) (addrs []string, err *os.Error) {
+func answer(name string, dns *DNS_Msg) (addrs []string, err *os.Error) {
  	addrs = make([]string, 0, len(dns.answer));

この変更は、netパッケージ内のほぼ全てのファイルにわたって行われ、_PackStructValuepackStructValueに、_PollServerpollServerになるなど、多数のプライベートな関数、型、変数が新しい命名規約に準拠するように修正されました。これにより、コードの見た目がよりクリーンになり、Goの命名規約がより明確に反映されるようになりました。

2. netFD型へのリネームと機能拡張

src/lib/net/fd.goでは、ネットワークファイルディスクリプタを管理するFD型がnetFDにリネームされました。これは上記の命名規約変更の一環ですが、単なるリネームに留まらず、netFD型にnet, laddr, raddrという新しいフィールドが追加されています。

--- a/src/lib/net/fd.go
+++ b/src/lib/net/fd.go
@@ -13,21 +13,23 @@ import (
 	"syscall";
 )
 
-// Network file descriptor.  Only intended to be used internally,
-// but have to export to make it available in other files implementing package net.\
-type FD struct {
+// Network file descriptor.
+type netFD struct {
 	// immutable until Close
 	fd int64;
 	osfd *os.FD;
-	cr chan *FD;
-	cw chan *FD;
+	cr chan *netFD;
+	cw chan *netFD;
+	net string;
+	laddr string;
+	raddr string;
 
 	// owned by fd wait server
 	ncr, ncw int;
 }

これらの新しいフィールドは、ネットワーク接続のタイプ(例: "tcp")、ローカルアドレス、リモートアドレスをnetFDインスタンス自体に保持することで、デバッグやロギングの際に接続に関するより詳細な情報を提供できるようになります。特に、os.NewFDに渡されるname引数にこれらの情報が組み込まれることで、os.FDString()メソッドがより有用な出力を生成するようになります。

3. 初期Goコンパイラ(6g)の制約への対応

src/lib/net/dnsconfig.gosrc/lib/net/port.goでは、file := _Open(...)のようなショート変数宣言がvar file = open(...)という形式に変更されています。これには「// TODO(rsc): 6g won't let me use "file :="」というコメントが付随しています。

--- a/src/lib/net/dnsconfig.go
+++ b/src/lib/net/dnsconfig.go
@@ -27,7 +27,8 @@ type DNS_Config struct {
 // of the host name to get the default search domain.
 // We assume it's in resolv.conf anyway.
 func DNS_ReadConfig() *DNS_Config {
-	file := _Open("/etc/resolv.conf");
+	// TODO(rsc): 6g won't let me use "file :="
+	var file = open("/etc/resolv.conf");
 	if file == nil {
 		return nil
 	}

これは、Go言語の初期のコンパイラである6gが、特定の多値返却関数(この場合はopen関数)とショート変数宣言:=の組み合わせを正しく処理できなかったという、当時のコンパイラの制約を示しています。このため、一時的にvarキーワードを使った明示的な変数宣言にフォールバックする必要がありました。これはGo言語の進化の歴史における興味深いスナップショットであり、現在のGoコンパイラではこのような制約は存在しません。

4. os_test.goのファイル名リスト修正

src/lib/os/os_test.goでは、osパッケージ内のファイル名が以前に変更されたことに伴い、テストで参照されるファイル名リストが更新されています。

--- a/src/lib/os/os_test.go
+++ b/src/lib/os/os_test.go
@@ -13,12 +13,12 @@ import (
 var dot = []string(
 	"dir_amd64_darwin.go",
 	"dir_amd64_linux.go",
-	"os_env.go",
-	"os_error.go",
-	"os_file.go",
+	"env.go",
+	"error.go",
+	"file.go",
 	"os_test.go",
-	"os_time.go",
-	"os_types.go",
+	"time.go",
+	"types.go",
 	"stat_amd64_darwin.go",
 	"stat_amd64_linux.go"
 )

これは、osパッケージの内部構造が変更された際に、テストコードがその変更に追従し、テストの正確性を維持するための保守的な修正です。

これらの変更は、Go言語がその初期段階でいかに活発に開発され、言語設計と実装の両面で洗練されていったかを示しています。特に、命名規約の統一は、Goのシンプルで一貫したデザイン哲学を象徴する変更と言えるでしょう。

関連リンク

参考にした情報源リンク

  • Go言語のコミット履歴(GitHub):https://github.com/golang/go/commit/d8921c5294cd0698184dc05b2af949ae1b147d12
  • Go言語の初期のコンパイラに関する情報(例: 6g):Go言語の歴史に関するブログ記事や論文、またはGoのソースコードリポジトリ内の古いドキュメント。
  • Go言語の命名規約に関する一般的な情報源。
  • netパッケージとosパッケージの現在のドキュメント(Go言語公式)。
  • syscallパッケージの現在のドキュメント(Go言語公式)。
  • reflectパッケージの現在のドキュメント(Go言語公式)。
  • syncパッケージのOnceに関するドキュメント(Go言語公式)。