[インデックス 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文字: 最も一般的な慣習です。例えば、
User
型のレシーバはu
、Request
型のレシーバはr
とします。 - 短縮形: 型名が長い場合や、最初の1文字が他の変数と衝突する可能性がある場合は、意味が通じる範囲で短縮形を使用します。例えば、
FileHeader
型のレシーバはfh
、MultipartReader
型のレシーバはmr
などです。 - 一貫性: 同じ型に対する複数のメソッドでは、常に同じレシーバ名を使用します。
この慣習は、コードの簡潔さと可読性を高めることを目的としています。レシーバ名が短く一貫していることで、メソッドのシグネチャが読みやすくなり、コードをスキャンする際にレシーバが何を表しているかを素早く理解できます。
Goのドキュメンテーションツール (godoc)
Go言語には、godoc
という公式のドキュメンテーションツールがあります。これは、Goのソースコードから直接ドキュメントを生成します。godoc
は、関数、変数、定数、型、メソッドなどの定義と、それらに付随するコメントを解析して、HTML形式のドキュメントを生成します。
レシーバ名を含むメソッドのシグネチャは、godoc
によって生成されるドキュメントにそのまま表示されます。したがって、レシーバ名の一貫性は、生成されるドキュメントの見た目と品質に直接影響します。不統一なレシーバ名は、ドキュメントのプロフェッショナル感を損ない、読者に混乱を与える可能性があります。
技術的詳細
このコミットは、Go言語の標準ライブラリ内の複数のパッケージにわたって、メソッドのレシーバ名をGoの慣習に従って統一するという、比較的単純ながらも重要な変更を行っています。この変更の技術的な詳細は以下の点に集約されます。
-
レシーバ名の短縮化と一貫性:
*FileHeader
のレシーバfh
をh
に変更。*Decoder
のレシーバp
をd
に変更。*Part
のレシーバbp
をp
に変更。*Reader
(mime/multipart) のレシーバmr
をr
に変更。*Request
のレシーバreq
をr
に変更。*Response
のレシーバresp
をr
に変更。*Server
のレシーバs
をsrv
に変更。*URL
のレシーバurl
をu
に変更。
これらの変更は、レシーバ名がその型を簡潔に表すようにし、かつ、同じパッケージ内で複数のレシーバが存在する場合でも衝突を避けるように配慮されています。例えば、
net/http
パッケージではRequest
とResponse
の両方にr
を使用していますが、これはそれぞれのメソッドが異なる型に属するため、コンテキストで区別できると判断されたためです。一方、Server
のレシーバはs
ではなくsrv
とすることで、より明確にServer
型であることを示しています。 -
ドキュメントの可読性向上: この変更の主な動機は、
godoc
によって生成されるドキュメントの「美しさ」と「一貫性」を向上させることです。レシーバ名が統一されることで、メソッドのシグネチャがより整然と表示され、ドキュメント全体としての品質が向上します。これは、Go言語の設計哲学である「シンプルさ」と「明瞭さ」にも合致します。 -
コードの保守性向上: レシーバ名の統一は、コードベース全体のスタイルガイドを強化し、新しいコードを書く開発者にとっての模範となります。これにより、将来的に追加されるコードも同様の命名規則に従うようになり、コードベース全体の保守性が向上します。
-
影響範囲: この変更は、Go標準ライブラリの複数のパッケージ(
archive/zip
,encoding/xml
,mime/multipart
,net/http
,net/url
)にわたっています。これは、レシーバ名の不統一が広範囲にわたっていたことを示唆しています。しかし、変更内容はレシーバ名の置換のみであるため、機能的な影響は一切ありません。純粋にコードスタイルとドキュメンテーションの改善を目的としたリファクタリングです。
コアとなるコードの変更箇所
このコミットでは、以下の7つのファイルでレシーバ名の変更が行われています。
src/pkg/archive/zip/struct.go
src/pkg/encoding/xml/read.go
src/pkg/mime/multipart/multipart.go
src/pkg/net/http/request.go
src/pkg/net/http/response.go
src/pkg/net/http/server.go
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
に変更されました。h
はHeader
の頭文字としてより簡潔です。src/pkg/encoding/xml/read.go
:Decoder
型のレシーバがp
からd
に変更されました。d
はDecoder
の頭文字としてより直感的です。元のp
はparser
を意図していたのかもしれませんが、型名に合わせた変更です。src/pkg/mime/multipart/multipart.go
:Part
型のレシーバがbp
からp
に変更されました。bp
はbody part
の略かもしれませんが、p
で十分意味が通じます。Reader
型のレシーバがmr
からr
に変更されました。mr
はmultipart reader
の略かもしれませんが、r
で十分意味が通じます。
src/pkg/net/http/request.go
:Request
型のレシーバがreq
からr
に変更されました。r
はRequest
の頭文字としてより簡潔です。src/pkg/net/http/response.go
:Response
型のレシーバがresp
からr
に変更されました。r
はResponse
の頭文字としてより簡潔です。Request
とResponse
で同じr
を使用していますが、Goでは異なる型に属するメソッドであれば同じレシーバ名を使用しても問題ありません。src/pkg/net/http/server.go
:Server
型のレシーバがs
からsrv
に変更されました。s
もServer
の頭文字ですが、srv
の方がより明確にServer
を示し、他の単一文字変数との衝突を避ける意図があるかもしれません。src/pkg/net/url/url.go
:URL
型のレシーバがurl
からu
に変更されました。url
は変数名としては長すぎるため、u
という単一文字にすることでGoの慣習に沿っています。
これらの変更は、Goのコードスタイルガイドラインに沿って、レシーバ名をその型の短縮形または最初の1文字に統一することで、コードの視覚的なノイズを減らし、メソッドシグネチャの可読性を向上させています。これにより、godoc
で生成されるドキュメントもより一貫性があり、プロフェッショナルな見た目になります。
関連リンク
- Go Code Review Comments: https://go.dev/doc/effective_go#commentary (レシーバの命名に関する直接的な言及は少ないですが、Goのコーディングスタイル全般について述べられています。)
- Go Wiki - CodeReviewComments: https://github.com/golang/go/wiki/CodeReviewComments#receiver-names (レシーバの命名に関する具体的なガイドラインが記載されています。)
- Go Change List 5576056: https://golang.org/cl/5576056 (このコミットに対応するGoの変更リスト)
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のスタイルガイドおよびコードレビューコメント
- Go言語のレシーバに関する一般的な解説記事
- GitHubのコミット履歴と差分表示
- https://go.dev/doc/effective_go#commentary
- https://github.com/golang/go/wiki/CodeReviewComments#receiver-names
- https://golang.org/cl/5576056
- https://pkg.go.dev/ (Goパッケージのドキュメント)
- https://go.dev/blog/ (Go公式ブログ)
- https://www.ardanlabs.com/blog/2017/05/design-philosophy-on-receiver-types.html (Goのレシーバに関する設計哲学のブログ記事)
- https://www.geeksforgeeks.org/receivers-in-golang/ (Goのレシーバに関する解説記事)