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

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

このコミットは、Go言語の標準ライブラリにおけるレシーバ名の命名規則を統一することを目的としています。特に、生成されるドキュメントの可読性と一貫性を向上させることに焦点を当てています。

コミット

doc: use consistent receiver names, when it makes sense.

Makes for prettier docs.

R=golang-dev, dsymonds, r, rsc
CC=golang-dev
https://golang.org/cl/5576056

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

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

元コミット内容

このコミットの目的は、Go言語のドキュメントをより美しく、読みやすくするために、レシーバの命名を統一することです。具体的には、レシーバの型名の最初の1文字(または短縮形)を使用するというGoの慣習に従い、既存のコードベースのレシーバ名を修正しています。

変更の背景

Go言語では、メソッドを定義する際にレシーバを使用します。レシーバは、そのメソッドがどの型の値に対して呼び出されるかを指定します。慣習として、レシーバの変数名はその型の名前の最初の1文字(または短縮形)を使用することが推奨されています。例えば、*FileHeader 型のレシーバであれば fh*Decoder 型であれば d といった具合です。

このコミットが行われた2012年当時、Go言語はまだ比較的新しく、コードベース全体でこのような命名規則が完全に統一されていなかった可能性があります。ドキュメントはコードから自動生成されるため、レシーバ名が不統一だと、生成されるドキュメントの見た目や一貫性が損なわれることがあります。

この変更の背景には、Go言語の公式ドキュメントの品質向上と、コードベース全体のスタイルガイドの徹底という意図があります。一貫性のある命名は、コードの可読性を高めるだけでなく、開発者が新しいコードを書く際の指針ともなります。

前提知識の解説

Go言語のレシーバ

Go言語において、メソッドは特定の型に関連付けられた関数です。この関連付けは「レシーバ」と呼ばれる特別な引数によって行われます。レシーバは、メソッドが呼び出されるインスタンス(値またはポインタ)を表します。

レシーバの構文:

func (receiverName ReceiverType) MethodName(parameters) (returnValues) {
    // メソッドの本体
}
  • receiverName: レシーバの変数名。メソッド内でレシーバのインスタンスにアクセスするために使用されます。
  • ReceiverType: レシーバの型。値レシーバ (T) またはポインタレシーバ (*T) のいずれかです。

レシーバの命名規則(Goの慣習):

Goコミュニティでは、レシーバの変数名について以下の慣習が広く採用されています。

  1. 型名の最初の1文字: 最も一般的な慣習です。例えば、User 型のレシーバは uRequest 型のレシーバは r とします。
  2. 短縮形: 型名が長い場合や、最初の1文字が他の変数と衝突する可能性がある場合は、意味が通じる範囲で短縮形を使用します。例えば、FileHeader 型のレシーバは fhMultipartReader 型のレシーバは mr などです。
  3. 一貫性: 同じ型に対する複数のメソッドでは、常に同じレシーバ名を使用します。

この慣習は、コードの簡潔さと可読性を高めることを目的としています。レシーバ名が短く一貫していることで、メソッドのシグネチャが読みやすくなり、コードをスキャンする際にレシーバが何を表しているかを素早く理解できます。

Goのドキュメンテーションツール (godoc)

Go言語には、godoc という公式のドキュメンテーションツールがあります。これは、Goのソースコードから直接ドキュメントを生成します。godoc は、関数、変数、定数、型、メソッドなどの定義と、それらに付随するコメントを解析して、HTML形式のドキュメントを生成します。

レシーバ名を含むメソッドのシグネチャは、godoc によって生成されるドキュメントにそのまま表示されます。したがって、レシーバ名の一貫性は、生成されるドキュメントの見た目と品質に直接影響します。不統一なレシーバ名は、ドキュメントのプロフェッショナル感を損ない、読者に混乱を与える可能性があります。

技術的詳細

このコミットは、Go言語の標準ライブラリ内の複数のパッケージにわたって、メソッドのレシーバ名をGoの慣習に従って統一するという、比較的単純ながらも重要な変更を行っています。この変更の技術的な詳細は以下の点に集約されます。

  1. レシーバ名の短縮化と一貫性:

    • *FileHeader のレシーバ fhh に変更。
    • *Decoder のレシーバ pd に変更。
    • *Part のレシーバ bpp に変更。
    • *Reader (mime/multipart) のレシーバ mrr に変更。
    • *Request のレシーバ reqr に変更。
    • *Response のレシーバ respr に変更。
    • *Server のレシーバ ssrv に変更。
    • *URL のレシーバ urlu に変更。

    これらの変更は、レシーバ名がその型を簡潔に表すようにし、かつ、同じパッケージ内で複数のレシーバが存在する場合でも衝突を避けるように配慮されています。例えば、net/http パッケージでは RequestResponse の両方に r を使用していますが、これはそれぞれのメソッドが異なる型に属するため、コンテキストで区別できると判断されたためです。一方、Server のレシーバは s ではなく srv とすることで、より明確に Server 型であることを示しています。

  2. ドキュメントの可読性向上: この変更の主な動機は、godoc によって生成されるドキュメントの「美しさ」と「一貫性」を向上させることです。レシーバ名が統一されることで、メソッドのシグネチャがより整然と表示され、ドキュメント全体としての品質が向上します。これは、Go言語の設計哲学である「シンプルさ」と「明瞭さ」にも合致します。

  3. コードの保守性向上: レシーバ名の統一は、コードベース全体のスタイルガイドを強化し、新しいコードを書く開発者にとっての模範となります。これにより、将来的に追加されるコードも同様の命名規則に従うようになり、コードベース全体の保守性が向上します。

  4. 影響範囲: この変更は、Go標準ライブラリの複数のパッケージ(archive/zip, encoding/xml, mime/multipart, net/http, net/url)にわたっています。これは、レシーバ名の不統一が広範囲にわたっていたことを示唆しています。しかし、変更内容はレシーバ名の置換のみであるため、機能的な影響は一切ありません。純粋にコードスタイルとドキュメンテーションの改善を目的としたリファクタリングです。

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

このコミットでは、以下の7つのファイルでレシーバ名の変更が行われています。

  1. src/pkg/archive/zip/struct.go
  2. src/pkg/encoding/xml/read.go
  3. src/pkg/mime/multipart/multipart.go
  4. src/pkg/net/http/request.go
  5. src/pkg/net/http/response.go
  6. src/pkg/net/http/server.go
  7. src/pkg/net/url/url.go

以下に、各ファイルでの変更例を抜粋します。

src/pkg/archive/zip/struct.go

--- a/src/pkg/archive/zip/struct.go
+++ b/src/pkg/archive/zip/struct.go
@@ -57,8 +57,8 @@ type FileHeader struct {
 }
 
 // FileInfo returns an os.FileInfo for the FileHeader.
-func (fh *FileHeader) FileInfo() os.FileInfo {
-	return headerFileInfo{fh}
+func (h *FileHeader) FileInfo() os.FileInfo {
+	return headerFileInfo{h}
 }
 
 // headerFileInfo implements os.FileInfo.

src/pkg/encoding/xml/read.go

--- a/src/pkg/encoding/xml/read.go
+++ b/src/pkg/encoding/xml/read.go
@@ -546,15 +546,15 @@ Loop:
 // Read tokens until we find the end element.
 // Token is taking care of making sure the
 // end element matches the start element we saw.
-func (p *Decoder) Skip() error {
+func (d *Decoder) Skip() error {
 	for {
-		tok, err := p.Token()
+		tok, err := d.Token()
 		if err != nil {
 			return err
 		}
 		switch tok.(type) {
 		case StartElement:
-			if err := p.Skip(); err != nil {
+			if err := d.Skip(); err != nil {
 				return err
 			}
 		case EndElement:

src/pkg/mime/multipart/multipart.go

--- a/src/pkg/mime/multipart/multipart.go
+++ b/src/pkg/mime/multipart/multipart.go
@@ -112,13 +112,13 @@ func (bp *Part) populateHeaders() error {
 
 // Read reads the body of a part, after its headers and before the
 // next part (if any) begins.
-func (bp *Part) Read(p []byte) (n int, err error) {
-	if bp.buffer.Len() >= len(p) {
+func (p *Part) Read(d []byte) (n int, err error) {
+	if p.buffer.Len() >= len(d) {
 		// Internal buffer of unconsumed data is large enough for
 		// the read request.  No need to parse more at the moment.
-		return bp.buffer.Read(p)
+		return p.buffer.Read(d)
 	}
-	peek, err := bp.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor
+	peek, err := p.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor
 	unexpectedEof := err == io.EOF
 	if err != nil && !unexpectedEof {
 		return 0, fmt.Errorf("multipart: Part Read: %v", err)
@@ -133,10 +133,10 @@ func (bp *Part) Read(p []byte) (n int, err error) {
 	// string.
 	nCopy := 0
 	foundBoundary := false
-	if idx := bytes.Index(peek, bp.mr.nlDashBoundary); idx != -1 {
+	if idx := bytes.Index(peek, p.mr.nlDashBoundary); idx != -1 {
 		nCopy = idx
 		foundBoundary = true
-	} else if safeCount := len(peek) - len(bp.mr.nlDashBoundary); safeCount > 0 {
+	} else if safeCount := len(peek) - len(p.mr.nlDashBoundary); safeCount > 0 {
 		nCopy = safeCount
 	} else if unexpectedEof {
 		// If we've run out of peek buffer and the boundary
@@ -145,11 +145,11 @@ func (bp *Part) Read(p []byte) (n int, err error) {
 		return 0, io.ErrUnexpectedEOF
 	}
 	if nCopy > 0 {
-		if _, err := io.CopyN(bp.buffer, bp.mr.bufReader, int64(nCopy)); err != nil {
+		if _, err := io.CopyN(p.buffer, p.mr.bufReader, int64(nCopy)); err != nil {
 			return 0, err
 		}
 	}
-	n, err = bp.buffer.Read(p)
+	n, err = p.buffer.Read(d)
 	if err == io.EOF && !foundBoundary {
 		// If the boundary hasn't been reached there's more to
 		// read, so don't pass through an EOF from the buffer
@@ -158,8 +158,8 @@ func (bp *Part) Read(p []byte) (n int, err error) {
 	return
 }
 
-func (bp *Part) Close() error {
-	io.Copy(ioutil.Discard, bp)
+func (p *Part) Close() error {
+	io.Copy(ioutil.Discard, p)
 	return nil
 }
 
@@ -177,29 +177,29 @@ type Reader struct {
 
 // NextPart returns the next part in the multipart or an error.
 // When there are no more parts, the error io.EOF is returned.
-func (mr *Reader) NextPart() (*Part, error) {
-	if mr.currentPart != nil {
-		mr.currentPart.Close()
+func (r *Reader) NextPart() (*Part, error) {
+	if r.currentPart != nil {
+		r.currentPart.Close()
 	}
 
 	expectNewPart := false
 	for {
-		line, err := mr.bufReader.ReadSlice('\n')
+		line, err := r.bufReader.ReadSlice('\n')
 		if err != nil {
 			return nil, fmt.Errorf("multipart: NextPart: %v", err)
 		}
 
-		if mr.isBoundaryDelimiterLine(line) {
-			mr.partsRead++
-			bp, err := newPart(mr)
+		if r.isBoundaryDelimiterLine(line) {
+			r.partsRead++
+			bp, err := newPart(r)
 			if err != nil {
 				return nil, err
 			}
-			mr.currentPart = bp
+			r.currentPart = bp
 			return bp, nil
 		}
 
-		if hasPrefixThenNewline(line, mr.dashBoundaryDash) {
+		if hasPrefixThenNewline(line, r.dashBoundaryDash) {
 			// Expected EOF
 			return nil, io.EOF
 		}
@@ -208,7 +208,7 @@ func (mr *Reader) NextPart() (*Part, error) {
 			return nil, fmt.Errorf("multipart: expecting a new Part; got line %q", string(line))
 		}
 
-		if mr.partsRead == 0 {
+		if r.partsRead == 0 {
 			// skip line
 			continue
 		}
@@ -217,7 +217,7 @@ func (mr *Reader) NextPart() (*Part, error) {
 		// body of the previous part and the boundary line we
 		// now expect will follow. (either a new part or the
 		// end boundary)
-		if bytes.Equal(line, mr.nl) {
+		if bytes.Equal(line, r.nl) {
 			expectNewPart = true
 			continue
 		}

src/pkg/net/http/request.go

--- a/src/pkg/net/http/request.go
+++ b/src/pkg/net/http/request.go
@@ -272,18 +272,18 @@ func valueOrDefault(value, def string) string {
 const defaultUserAgent = "Go http package"
 
 // Write writes an HTTP/1.1 request -- header and body -- in wire format.
-// This method consults the following fields of req:
+// This method consults the following fields of the request:
 //	Host
 //	URL
 //	Method (defaults to "GET")
 //	Header
 //	Body
 // If Body is present, Content-Length is <= 0 and TransferEncoding
 // hasn't been set to "identity", Write adds "Transfer-Encoding:"
 // chunked" to the header. Body is closed after it is sent.
-func (req *Request) Write(w io.Writer) error {
-	return req.write(w, false, nil)
+func (r *Request) Write(w io.Writer) error {
+	return r.write(w, false, nil)
 }
 
 // WriteProxy is like Write but writes the request in the form
 // expected by an HTTP proxy.  In particular, WriteProxy writes the
 // initial Request-URI line of the request with an absolute URI, per
-// section 5.1.2 of RFC 2616, including the scheme and host. In
-// either case, WriteProxy also writes a Host header, using either
-// req.Host or req.URL.Host.
-func (req *Request) WriteProxy(w io.Writer) error {
-	return req.write(w, true, nil)
+// section 5.1.2 of RFC 2616, including the scheme and host.
+// In either case, WriteProxy also writes a Host header, using
+// either r.Host or r.URL.Host.
+func (r *Request) WriteProxy(w io.Writer) error {
+	return r.write(w, true, nil)
 }
 
 // extraHeaders may be nil

src/pkg/net/http/response.go

--- a/src/pkg/net/http/response.go
+++ b/src/pkg/net/http/response.go
@@ -174,7 +174,7 @@ func (r *Response) ProtoAtLeast(major, minor int) bool {
 }
 
 // Writes the response (header, body and trailer) in wire format. This method
-// consults the following fields of resp:
+// consults the following fields of the response:
 //
 //  StatusCode
 //  ProtoMajor
@@ -186,28 +186,28 @@ func (r *Response) ProtoAtLeast(major, minor int) bool {
 //  ContentLength
 //  Header, values for non-canonical keys will have unpredictable behavior
 //
-func (resp *Response) Write(w io.Writer) error {
+func (r *Response) Write(w io.Writer) error {
 
 	// RequestMethod should be upper-case
-	if resp.Request != nil {
-		resp.Request.Method = strings.ToUpper(resp.Request.Method)
+	if r.Request != nil {
+		r.Request.Method = strings.ToUpper(r.Request.Method)
 	}
 
 	// Status line
-	text := resp.Status
+	text := r.Status
 	if text == "" {
 		var ok bool
-		text, ok = statusText[resp.StatusCode]
+		text, ok = statusText[r.StatusCode]
 		if !ok {
-			text = "status code " + strconv.Itoa(resp.StatusCode)
+			text = "status code " + strconv.Itoa(r.StatusCode)
 		}
 	}
-	io.WriteString(w, "HTTP/"+strconv.Itoa(resp.ProtoMajor)+".")
-	io.WriteString(w, strconv.Itoa(resp.ProtoMinor)+" ")
-	io.WriteString(w, strconv.Itoa(resp.StatusCode)+" "+text+"\r\n")
+	io.WriteString(w, "HTTP/"+strconv.Itoa(r.ProtoMajor)+".")
+	io.WriteString(w, strconv.Itoa(r.ProtoMinor)+" ")
+	io.WriteString(w, strconv.Itoa(r.StatusCode)+" "+text+"\r\n")
 
 	// Process Body,ContentLength,Close,Trailer
-	tw, err := newTransferWriter(resp)
+	tw, err := newTransferWriter(r)
 	if err != nil {
 		return err
 	}
@@ -217,7 +217,7 @@ func (resp *Response) Write(w io.Writer) error {\
 	}
 
 	// Rest of header
-	err = resp.Header.WriteSubset(w, respExcludeHeader)
+	err = r.Header.WriteSubset(w, respExcludeHeader)
 	if err != nil {
 		return err
 	}

src/pkg/net/http/server.go

--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -1078,8 +1078,8 @@ func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Han
 // of the server's certificate followed by the CA's certificate.
 //
 // If srv.Addr is blank, ":https" is used.
-func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
-	addr := s.Addr
+func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
+	addr := srv.Addr
 	if addr == "" {
 		addr = ":https"
 	}
@@ -1101,7 +1101,7 @@ func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
 	}
 
 	tlsListener := tls.NewListener(conn, config)
-	return s.Serve(tlsListener)
+	return srv.Serve(tlsListener)
 }
 
 // TimeoutHandler returns a Handler that runs h with the given time limit.

src/pkg/net/url/url.go

--- a/src/pkg/net/url/url.go
+++ b/src/pkg/net/url/url.go
@@ -431,30 +431,30 @@ func ParseWithReference(rawurlref string) (url *URL, err error) {
 	return url, nil
 }
 
-// String reassembles url into a valid URL string.
-func (url *URL) String() string {
+// String reassembles the URL into a valid URL string.
+func (u *URL) String() string {
 	// TODO: Rewrite to use bytes.Buffer
 	result := ""
-	if url.Scheme != "" {
-		result += url.Scheme + ":"
+	if u.Scheme != "" {
+		result += u.Scheme + ":"
 	}
-	if url.Opaque != "" {
-		result += url.Opaque
+	if u.Opaque != "" {
+		result += u.Opaque
 	} else {
-		if url.Host != "" || url.User != nil {
+		if u.Host != "" || u.User != nil {
 			result += "//"
-			if u := url.User; u != nil {
+			if u := u.User; u != nil {
 				result += u.String() + "@"
 			}
-			result += url.Host
+			result += u.Host
 		}
-		result += escape(url.Path, encodePath)
+		result += escape(u.Path, encodePath)
 	}
-	if url.RawQuery != "" {
-		result += "?" + url.RawQuery
+	if u.RawQuery != "" {
+		result += "?" + u.RawQuery
 	}
-	if url.Fragment != "" {
-		result += "#" + escape(url.Fragment, encodeFragment)
+	if u.Fragment != "" {
+		result += "#" + escape(u.Fragment, encodeFragment)
 	}
 	return result
 }
@@ -585,8 +585,8 @@ func resolvePath(basepath string, refpath string) string {
 }
 
 // IsAbs returns true if the URL is absolute.
-func (url *URL) IsAbs() bool {
-	return url.Scheme != ""
+func (u *URL) IsAbs() bool {
+	return u.Scheme != ""
 }
 
 // Parse parses a URL in the context of a base URL.  The URL in ref

コアとなるコードの解説

上記の変更箇所はすべて、メソッド定義におけるレシーバの変数名を変更しているだけです。機能的な変更は一切なく、純粋にコードのスタイルと一貫性を向上させるためのものです。

具体的には、以下のような変更が行われています。

  • src/pkg/archive/zip/struct.go: FileHeader 型のレシーバが fh から h に変更されました。hHeader の頭文字としてより簡潔です。
  • src/pkg/encoding/xml/read.go: Decoder 型のレシーバが p から d に変更されました。dDecoder の頭文字としてより直感的です。元の pparser を意図していたのかもしれませんが、型名に合わせた変更です。
  • src/pkg/mime/multipart/multipart.go:
    • Part 型のレシーバが bp から p に変更されました。bpbody part の略かもしれませんが、p で十分意味が通じます。
    • Reader 型のレシーバが mr から r に変更されました。mrmultipart reader の略かもしれませんが、r で十分意味が通じます。
  • src/pkg/net/http/request.go: Request 型のレシーバが req から r に変更されました。rRequest の頭文字としてより簡潔です。
  • src/pkg/net/http/response.go: Response 型のレシーバが resp から r に変更されました。rResponse の頭文字としてより簡潔です。RequestResponse で同じ r を使用していますが、Goでは異なる型に属するメソッドであれば同じレシーバ名を使用しても問題ありません。
  • src/pkg/net/http/server.go: Server 型のレシーバが s から srv に変更されました。sServer の頭文字ですが、srv の方がより明確に Server を示し、他の単一文字変数との衝突を避ける意図があるかもしれません。
  • src/pkg/net/url/url.go: URL 型のレシーバが url から u に変更されました。url は変数名としては長すぎるため、u という単一文字にすることでGoの慣習に沿っています。

これらの変更は、Goのコードスタイルガイドラインに沿って、レシーバ名をその型の短縮形または最初の1文字に統一することで、コードの視覚的なノイズを減らし、メソッドシグネチャの可読性を向上させています。これにより、godoc で生成されるドキュメントもより一貫性があり、プロフェッショナルな見た目になります。

関連リンク

参考にした情報源リンク