[インデックス 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
インターフェースを完全に実装していませんでした。
このコミットでは、以下の修正が行われています。
- フィールド名の変更:
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 }
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) {
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言語の
net
パッケージドキュメント: https://pkg.go.dev/net - Go言語の
net.Listener
インターフェース: https://pkg.go.dev/net#Listener - Go言語の
exp/ssh
パッケージドキュメント (当時のバージョンに近いもの): 検索結果から適切な時期のドキュメントを探す必要がありますが、一般的には https://pkg.go.dev/golang.org/x/crypto/ssh が後継となります。
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコードリポジトリ (GitHub)
- Go言語のコードレビューシステム (Gerrit) - CL 5436056 および CL 5437090 の詳細な変更履歴や議論を参照することで、より深い背景情報を得ることができます。
- CL 5436056: https://golang.org/cl/5436056
- CL 5437090: https://golang.org/cl/5437090
- Go言語の設計原則に関する一般的な情報源 (例: Effective Go, Go Proverbsなど)