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

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

このコミットは、Go言語の実験的なSSHパッケージ (exp/ssh) における変更を取り消すものです。具体的には、以前のコミットで誤ってエクスポートされてしまった net.Listener 型のフィールドを、再度非エクスポート(プライベート)に戻すとともに、net.Listener インターフェースを満たすために必要な Addr() および Close() メソッドを Listener 型に再追加しています。これにより、exp/ssh パッケージの Listener 型が net.Listener インターフェースを正しく実装し、かつ内部実装の詳細が外部に漏れないように修正されています。

コミット

commit 0e62c75b9d6e96a24c5a0a933c6a634a4595d62a
Author: Dave Cheney <dave@cheney.net>
Date:   Wed Nov 30 17:14:03 2011 -0500

    undo CL 5436056 / 03560deae933
    
    Remove the accidentally exported net.Listener
    
    ««« original CL description
    exp/ssh: remove unused forwarding methods in Server Listener
    
    R=agl, rsc
    CC=golang-dev
    https://golang.org/cl/5436056
    
    »»»
    
    R=agl, rsc
    CC=golang-dev
    https://golang.org/cl/5437090

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

https://github.com/golang/go/commit/0e62c75b9d6e96a24c5a0a933c6a634a4595d62a

元コミット内容

このコミットは、CL 5436056 (コミットハッシュ 03560deae933) の変更を取り消すものです。元のコミットの目的は「exp/ssh: remove unused forwarding methods in Server Listener」(Server Listener内の未使用の転送メソッドを削除する)でした。この元のコミットの意図は、Server Listenerから不要なコードを削除することでしたが、その過程で意図せず net.Listener 型のフィールドがエクスポートされてしまい、さらに net.Listener インターフェースを満たすために必要なメソッドも削除されてしまったと考えられます。

変更の背景

このコミットの背景には、Go言語の設計原則である「エクスポートされた識別子(大文字で始まるフィールドやメソッド)は、パッケージの外部からアクセス可能になるため、APIの一部として公開される」という考え方があります。

元のコミット 03560deae933 では、exp/ssh パッケージの Listener 型から「未使用の転送メソッド」を削除する意図がありました。しかし、その変更の副作用として、Listener 型が内部に持つ net.Listener 型のフィールドが net.Listener という名前でエクスポートされてしまいました。Go言語では、構造体のフィールド名が大文字で始まる場合、そのフィールドはエクスポートされ、パッケージの外部から直接アクセス可能になります。これは、Listener 型の内部実装の詳細が外部に公開されてしまうことを意味し、APIの安定性やカプセル化の観点から望ましくありません。

さらに、net.Listener インターフェースを実装するためには、Accept(), Addr(), Close() の3つのメソッドが必要です。元のコミットでは、Listener 型が net.Listener インターフェースを実装しているにもかかわらず、Addr()Close() メソッドが削除されてしまった可能性があります。これにより、Listener 型は net.Listener インターフェースの要件を満たさなくなり、net.Listener として利用できなくなるという問題が発生しました。

このコミットは、これらの問題を修正し、Listener 型が net.Listener インターフェースを正しく実装しつつ、内部実装の詳細を隠蔽するという、Go言語の設計原則に沿った状態に戻すことを目的としています。

前提知識の解説

  • Go言語のエクスポートルール: Go言語では、識別子(変数名、関数名、型名、構造体のフィールド名など)の最初の文字が大文字で始まる場合、その識別子はパッケージの外部にエクスポートされ、他のパッケージからアクセス可能になります。小文字で始まる場合は、そのパッケージ内でのみアクセス可能なプライベートな識別子となります。
  • net.Listener インターフェース: Go言語の標準ライブラリ net パッケージには、ネットワーク接続をリッスンするための Listener インターフェースが定義されています。このインターフェースは以下の3つのメソッドを持ちます。
    • Accept() (Conn, error): 次の着信接続を待ち受け、それを返します。
    • Close() error: リスナーを閉じます。
    • Addr() Addr: リスナーのネットワークアドレスを返します。 net.Listener インターフェースを実装する型は、これらのメソッドをすべて持っている必要があります。
  • exp/ssh パッケージ: exp/ssh は、Go言語の実験的なSSH(Secure Shell)プロトコル実装を提供するパッケージです。実験的なパッケージであるため、APIが安定しておらず、将来的に変更される可能性があります。このパッケージは、SSHサーバーやクライアントの実装に利用されます。
  • 構造体の埋め込み (Embedded fields): Go言語では、構造体の中に別の構造体やインターフェースをフィールドとして宣言することができます。この場合、埋め込まれた型のメソッドは、外側の構造体のメソッドとして「昇格」され、直接呼び出すことができます。例えば、type Listener struct { net.Listener } のように宣言すると、Listener 型のインスタンスから l.Accept() のように net.Listener のメソッドを直接呼び出すことができます。これは、インターフェースの実装を簡潔にするためによく用いられる手法です。

技術的詳細

このコミットは、src/pkg/exp/ssh/server.go ファイル内の Listener 構造体とその関連メソッドに対する変更です。

元のコミット 03560deae933 では、Listener 構造体の定義が以下のように変更されたと考えられます。

type Listener struct {
	net.Listener // フィールド名がnet.Listenerとなり、エクスポートされてしまう
	config *ServerConfig
}

この変更により、Listener 型のインスタンスから l.net.Listener のように内部の net.Listener インスタンスに直接アクセスできるようになってしまい、カプセル化が破られていました。また、net.Listener インターフェースの Addr()Close() メソッドが Listener 型から削除されたため、Listener 型はもはや net.Listener インターフェースを完全に実装していませんでした。

このコミットでは、以下の修正が行われています。

  1. フィールド名の変更: Listener 構造体内の net.Listener 型のフィールド名を net.Listener から listener に変更しています。これにより、フィールドが小文字で始まるため、パッケージ外部からはアクセスできないプライベートなフィールドとなり、カプセル化が維持されます。
    --- a/src/pkg/exp/ssh/server.go
    +++ b/src/pkg/exp/ssh/server.go
    @@ -636,15 +636,15 @@ func (s *ServerConn) Accept() (Channel, error) {
     
     // A Listener implements a network listener (net.Listener) for SSH connections.
     type Listener struct {
    -	net.Listener
    -	config *ServerConfig
    +	listener net.Listener
    +	config   *ServerConfig
     }
    
  2. Accept() メソッドの修正: Accept() メソッド内で、内部の net.Listener インスタンスへのアクセスが l.Listener.Accept() から l.listener.Accept() に変更されています。
    --- a/src/pkg/exp/ssh/server.go
    +++ b/src/pkg/exp/ssh/server.go
    @@ -636,15 +636,15 @@ func (s *ServerConn) Accept() (Channel, error) {
     
     // A Listener implements a network listener (net.Listener) for SSH connections.
     type Listener struct {
    -	net.Listener
    -	config *ServerConfig
    +	listener net.Listener
    +	config   *ServerConfig
     }
     
     // Accept waits for and returns the next incoming SSH connection.
     // The receiver should call Handshake() in another goroutine 
     // to avoid blocking the accepter.\n func (l *Listener) Accept() (*ServerConn, error) {
    -	c, err := l.Listener.Accept()\n+\tc, err := l.listener.Accept()\n \tif err != nil {
     	\treturn nil, err
     	}\n@@ -652,6 +652,16 @@ func (l *Listener) Accept() (*ServerConn, error) {
     	return conn, nil
     }\n \n+// Addr returns the listener\'s network address.\n+func (l *Listener) Addr() net.Addr {\n+\treturn l.listener.Addr()\n+}\n+\n+// Close closes the listener.\n+func (l *Listener) Close() error {\n+\treturn l.listener.Close()\n+}\n+\n // Listen creates an SSH listener accepting connections on\n // the given network address using net.Listen.\n func Listen(network, addr string, config *ServerConfig) (*Listener, error) {
    
  3. Addr() および Close() メソッドの再追加: net.Listener インターフェースを完全に実装するために必要な Addr()Close() メソッドが Listener 型に再追加されています。これらのメソッドは、内部の listener フィールドの対応するメソッドを呼び出すことで、net.Listener インターフェースの要件を満たしています。

これらの変更により、Listener 型は net.Listener インターフェースを正しく実装し、かつその内部実装が適切にカプセル化されるようになりました。

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

src/pkg/exp/ssh/server.go ファイルにおいて、以下の変更が行われました。

--- a/src/pkg/exp/ssh/server.go
+++ b/src/pkg/exp/ssh/server.go
@@ -636,15 +636,15 @@ func (s *ServerConn) Accept() (Channel, error) {
 
 // A Listener implements a network listener (net.Listener) for SSH connections.
 type Listener struct {
-	net.Listener
-	config *ServerConfig
+	listener net.Listener
+	config   *ServerConfig
 }
 
 // Accept waits for and returns the next incoming SSH connection.
 // The receiver should call Handshake() in another goroutine 
 // to avoid blocking the accepter.
 func (l *Listener) Accept() (*ServerConn, error) {
-	c, err := l.Listener.Accept()
+	c, err := l.listener.Accept()
 	if err != nil {
 		return nil, err
 	}
@@ -652,6 +652,16 @@ func (l *Listener) Accept() (*ServerConn, error) {
 	return conn, nil
 }
 
+// Addr returns the listener's network address.
+func (l *Listener) Addr() net.Addr {
+	return l.listener.Addr()
+}
+
+// Close closes the listener.
+func (l *Listener) Close() error {
+	return l.listener.Close()
+}
+
 // Listen creates an SSH listener accepting connections on
 // the given network address using net.Listen.
 func Listen(network, addr string, config *ServerConfig) (*Listener, error) {

コアとなるコードの解説

  • type Listener struct { ... }:
    • - net.Listener: 以前は net.Listener 型のフィールドが net.Listener という名前で埋め込まれていました。これにより、このフィールドはエクスポートされ、外部から直接アクセス可能になっていました。
    • + listener net.Listener: フィールド名が listener に変更されました。小文字で始まるため、このフィールドはプライベートとなり、Listener 型の内部実装の詳細が外部に公開されなくなりました。これはカプセル化の原則に則った修正です。
  • func (l *Listener) Accept() (*ServerConn, error) { ... }:
    • c, err := l.Listener.Accept() から c, err := l.listener.Accept() へ変更されました。これは、Listener 構造体内の net.Listener インスタンスへのアクセス方法が、エクスポートされたフィールド名からプライベートなフィールド名に変更されたことに対応する修正です。
  • func (l *Listener) Addr() net.Addr { ... }:
    • このメソッドは新しく追加されました。net.Listener インターフェースの Addr() メソッドを実装するために必要です。内部のプライベートな listener フィールドの Addr() メソッドを呼び出すことで、Listener 型が net.Listener インターフェースの要件を満たすようにしています。
  • func (l *Listener) Close() error { ... }:
    • このメソッドも新しく追加されました。net.Listener インターフェースの Close() メソッドを実装するために必要です。内部のプライベートな listener フィールドの Close() メソッドを呼び出すことで、Listener 型が net.Listener インターフェースの要件を満たすようにしています。

これらの変更により、exp/ssh パッケージの Listener 型は、net.Listener インターフェースを正しく実装し、かつその内部実装が適切にカプセル化されるという、Go言語の設計原則に沿った状態に戻されました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコードリポジトリ (GitHub)
  • Go言語のコードレビューシステム (Gerrit) - CL 5436056 および CL 5437090 の詳細な変更履歴や議論を参照することで、より深い背景情報を得ることができます。
  • Go言語の設計原則に関する一般的な情報源 (例: Effective Go, Go Proverbsなど)