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

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

このコミットは、Go言語の標準ライブラリである net パッケージ内の ListenTCP 関数の実装を簡素化するものです。具体的には、TCPListener オブジェクトの生成方法をより簡潔な形式に修正しています。

コミット

commit 19d793a32771ab8f9e64c67b792cd4cddacb679c
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Mon Dec 3 20:00:50 2012 +0900

    net: simplify ListenTCP
    
    R=golang-dev, dave
    CC=golang-dev
    https://golang.org/cl/6875044

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

https://github.com/golang/go/commit/19d793a32771ab8f9e64c67b792cd4cddacb679c

元コミット内容

net: simplify ListenTCP

このコミットは、Go言語の net パッケージにおける ListenTCP 関数の実装を簡素化することを目的としています。

変更の背景

Go言語では、コードの簡潔さ、可読性、そして効率性が重視されます。このコミットは、ListenTCP 関数内で TCPListener 構造体のインスタンスを生成し、そのフィールドに値を割り当てる一連の操作を、より直接的で簡潔な複合リテラル(composite literal)による初期化に置き換えることで、これらの原則に沿った改善を図っています。

従来のコードでは、まず new(TCPListener) でポインタを割り当て、次にそのポインタが指す構造体の fd フィールドに値を代入し、最後にそのポインタを返していました。この一連の操作は、Goの複合リテラルを使用することで、単一の式で表現可能です。これにより、コードの行数を削減し、意図をより明確に伝えることができます。

前提知識の解説

  • Go言語の net パッケージ: Go言語の標準ライブラリの一部で、ネットワークプログラミングのための基本的な機能を提供します。TCP/IP、UDP、Unixドメインソケットなどのネットワーク通信を扱うための型や関数が含まれています。
  • TCPListener 構造体: net パッケージで定義されている型で、TCPネットワーク接続をリッスン(待ち受け)するためのオブジェクトを表します。クライアントからの接続要求を受け入れ、新しい TCPConn オブジェクトを返す役割を担います。内部的には、ネットワークソケットのファイルディスクリプタ(fd)などの情報を持っています。
  • TCPAddr 構造体: TCPネットワークアドレスを表す型です。IPアドレスとポート番号を含みます。
  • OpError 構造体: net パッケージで定義されているエラー型で、ネットワーク操作中に発生したエラーの詳細(操作の種類、ネットワークタイプ、アドレス、元のエラーなど)を提供します。
  • ファイルディスクリプタ (File Descriptor, fd): オペレーティングシステムがファイルやソケットなどのI/Oリソースを識別するために使用する抽象的なハンドルです。Goの net パッケージでは、ソケット操作のために内部的にこれらを管理します。
  • 複合リテラル (Composite Literal): Go言語の機能の一つで、構造体、配列、スライス、マップなどの複合型を初期化するための簡潔な構文です。&TypeName{field1: value1, field2: value2} のように記述することで、新しいインスタンスを生成し、同時にそのフィールドを初期化できます。new(TypeName) でゼロ値のポインタを生成し、後からフィールドに値を代入するよりも、コードが簡潔になります。

技術的詳細

このコミットの技術的な詳細は、Go言語における構造体の初期化方法の最適化にあります。

変更前は、ListenTCP 関数内で TCPListener のインスタンスを生成する際に、以下の2ステップを踏んでいました。

  1. l := new(TCPListener): new キーワードを使用して TCPListener 型のゼロ値のポインタを割り当て、そのポインタを l に代入します。この時点では l.fd はゼロ値(nil)です。
  2. l.fd = fd: 割り当てられた TCPListener インスタンスの fd フィールドに、ソケット操作で得られた fd の値を明示的に代入します。

この2ステップは、Goの複合リテラルを使用することで、単一の式にまとめることができます。

変更後は、return &TCPListener{fd}, nil という形式になっています。 ここで、&TCPListener{fd} は複合リテラルであり、以下の処理を一度に行います。

  1. 新しい TCPListener 構造体のインスタンスを生成します。
  2. そのインスタンスの fd フィールドに、引数として渡された fd の値を直接初期値として設定します。
  3. 生成された TCPListener インスタンスへのポインタを返します。

この変更により、コードの行数が3行から1行に削減され、TCPListener オブジェクトの生成と初期化がよりアトミックかつ明確になりました。これは、Goのイディオムに沿った、よりクリーンなコード記述方法とされています。パフォーマンスへの影響はごくわずかですが、コンパイラが最適化しやすくなる可能性もあります。

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

変更は src/pkg/net/tcpsock_posix.go ファイルの ListenTCP 関数内で行われています。

--- a/src/pkg/net/tcpsock_posix.go
+++ b/src/pkg/net/tcpsock_posix.go
@@ -299,7 +299,5 @@ func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
 		closesocket(fd.sysfd)
 		return nil, &OpError{"listen", net, laddr, err}
 	}\n-\tl := new(TCPListener)\n-\tl.fd = fd\n-\treturn l, nil\n+\treturn &TCPListener{fd}, nil
 }\n

コアとなるコードの解説

ListenTCP 関数は、指定されたネットワークタイプ(例: "tcp", "tcp4", "tcp6")とローカルアドレス (laddr) を使用して、TCPネットワーク接続をリッスンするための TCPListener オブジェクトを生成します。

変更前のコードでは、TCPListener オブジェクトを返す直前に、以下のように記述されていました。

	l := new(TCPListener) // TCPListenerのポインタを割り当て
	l.fd = fd             // fdフィールドに値を代入
	return l, nil         // ポインタを返す

これは、TCPListener のインスタンスを生成し、その fd フィールドにソケットのファイルディスクリプタを割り当ててから、そのインスタンスへのポインタを返していました。

変更後のコードでは、この3行が1行に集約されました。

	return &TCPListener{fd}, nil // 複合リテラルでインスタンスを生成し、fdを初期化してポインタを返す

この新しい記述では、&TCPListener{fd} という複合リテラルが使用されています。これは、TCPListener 型の新しいインスタンスを生成し、その fd フィールドを直接 fd 変数の値で初期化することを意味します。結果として得られるのは TCPListener 構造体へのポインタであり、これが関数から返されます。

この変更は、機能的な振る舞いを一切変えることなく、コードの表現をより簡潔でGoらしいイディオムに沿ったものにしています。

関連リンク

  • Go Code Review: https://golang.org/cl/6875044

参考にした情報源リンク

  • 特になし (コミット内容とGo言語の一般的な知識に基づいて解説を生成しました。)