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

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

このコミットは、Go言語のgo/scannerパッケージ内のレシーバ変数名を、よりGoらしい(idiomatic)命名規則に準拠するように変更するものです。具体的には、Scanner構造体のメソッド内でレシーバとして使用されていた大文字のSを、小文字のsに変更しています。

コミット

commit 77f11f3ef12ecd55a0d6386c83fe727f7abb3879
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Feb 2 16:42:29 2012 -0800

    go/scanner: idiomatic receiver names
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5606057

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

https://github.com/golang/go/commit/77f11f3ef12ecd55a0d6386c83fe727f7abb3879

元コミット内容

go/scanner: idiomatic receiver names

R=rsc
CC=golang-dev
https://golang.org/cl/5606057

変更の背景

この変更の背景には、Go言語における「慣用的な(idiomatic)」コーディングスタイル、特にレシーバ変数の命名規則があります。Go言語の公式ドキュメントやコミュニティの慣習では、メソッドのレシーバ変数は、その型名の最初の1文字(または数文字)を小文字にしたものを使用することが推奨されています。

このコミットが行われた2012年2月時点では、Go言語はまだ比較的新しい言語であり、コーディングスタイルや慣習が確立されつつある段階でした。go/scannerパッケージは、Goのソースコードを字句解析(スキャン)するための重要なコンポーネントであり、Goコンパイラやその他のツールで利用されます。このような基盤となるパッケージがGoの慣習に沿っていることは、言語全体の整合性と可読性を高める上で重要です。

以前のコードでは、Scanner構造体のレシーバにSという大文字の変数名が使われていました。これは、Goの慣習からすると少し逸脱していました。このコミットは、この命名をsという小文字に変更することで、Goコミュニティで広く受け入れられている慣習に合わせることを目的としています。これにより、コードの可読性が向上し、Go言語の他の部分との一貫性が保たれます。

前提知識の解説

Go言語のレシーバ

Go言語では、関数を型に関連付けることで「メソッド」を定義します。この際、メソッドが操作するインスタンスは「レシーバ」と呼ばれ、メソッドの定義時にレシーバ変数を指定します。レシーバは、C++のthisやJavaのthisに似ていますが、Goでは明示的に名前を付けます。

レシーバには、値レシーバとポインタレシーバの2種類があります。

  • 値レシーバ: func (s MyStruct) MyMethod() {} のように定義されます。メソッド内でレシーバのフィールドを変更しても、元のインスタンスには影響しません(コピーが渡されるため)。
  • ポインタレシーバ: func (s *MyStruct) MyMethod() {} のように定義されます。メソッド内でレシーバのフィールドを変更すると、元のインスタンスに影響します(ポインタが渡されるため)。

このコミットでは、*Scannerというポインタレシーバが使用されています。これは、Scannerの内部状態(ch, offset, rdOffsetなど)がメソッド内で変更されるため、ポインタレシーバが適切だからです。

Go言語の慣用的な命名規則(Idiomatic Naming Conventions)

Go言語には、コードの可読性と一貫性を高めるためのいくつかの命名規則があります。

  • パッケージ名: 短く、すべて小文字で、単数形。
  • 変数名: 短く、意味が明確なもの。特にループ変数や一時変数は1文字が許容される。
  • 関数名/メソッド名: CamelCase。エクスポートされる(外部からアクセス可能な)ものは大文字で始まる。
  • レシーバ名: メソッドのレシーバ変数は、その型名の最初の1文字(または数文字)を小文字にしたものを使用するのが慣習です。例えば、type Reader struct {} のメソッドであれば (r *Reader)type Buffer struct {} であれば (b *Buffer) のようにします。これは、レシーバがメソッドの主要な引数であることを示しつつ、コードの行を不必要に長くしないためのものです。

このコミットは、まさにこの「レシーバ名」の慣習に準拠するための変更です。Scanner型の場合、慣習的にはsがレシーバ名として適切です。

go/scannerパッケージ

go/scannerパッケージは、Go言語のソースコードを字句解析(lexical analysis)するための機能を提供します。字句解析とは、ソースコードをトークン(識別子、キーワード、演算子、リテラルなど)のストリームに分解するプロセスです。これは、コンパイラやリンター、フォーマッターなどのツールがソースコードを理解するための最初のステップです。

Scanner構造体は、この字句解析の状態を保持し、Scan()メソッドによって次のトークンを読み取ります。内部的には、ソースコードのバイト列を読み込み、Unicode文字にデコードし、コメントや文字列、数値などを識別して適切なトークンを生成します。

技術的詳細

このコミットの技術的な変更は非常にシンプルで、src/pkg/go/scanner/scanner.goファイル内のScanner構造体のすべてのメソッドのレシーバ変数名を、Sからsに変更することです。

例えば、変更前は以下のようになっていました。

func (S *Scanner) next() {
    // ...
}

これが変更後には以下のようになります。

func (s *Scanner) next() {
    // ...
}

この変更は、メソッドのシグネチャだけでなく、メソッド本体内でレシーバ変数Sを参照しているすべての箇所に適用されています。これにより、コード全体の整合性が保たれます。

この変更は、機能的な影響は一切ありません。プログラムの動作は変更されず、単にコードのスタイルと可読性を向上させるためのものです。しかし、Go言語の慣習に準拠することで、将来のコードの保守性や、他のGo開発者による理解を容易にするという点で重要な意味を持ちます。

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

変更はsrc/pkg/go/scanner/scanner.goファイル全体にわたっています。具体的には、Scanner構造体のすべてのメソッド定義において、レシーバ変数名がSからsに変更され、それに伴いメソッド本体内のSへの参照もすべてsに変更されています。

以下に、変更の例をいくつか示します。

next() メソッドの変更:

--- a/src/pkg/go/scanner/scanner.go
+++ b/src/pkg/go/scanner/scanner.go
@@ -53,36 +53,36 @@ type Scanner struct {
 	ErrorCount int // number of errors encountered
 }
 
-// Read the next Unicode char into S.ch.
-// S.ch < 0 means end-of-file.
+// Read the next Unicode char into s.ch.
+// s.ch < 0 means end-of-file.
 //
-func (S *Scanner) next() {
-	if S.rdOffset < len(S.src) {
-		S.offset = S.rdOffset
-		if S.ch == '\n' {
-			S.lineOffset = S.offset
-			S.file.AddLine(S.offset)
+func (s *Scanner) next() {
+	if s.rdOffset < len(s.src) {
+		s.offset = s.rdOffset
+		if s.ch == '\n' {
+			s.lineOffset = s.offset
+			s.file.AddLine(s.offset)
 		}
-		r, w := rune(S.src[S.rdOffset]), 1
+		r, w := rune(s.src[s.rdOffset]), 1
 		switch {
 		case r == 0:
-			S.error(S.offset, "illegal character NUL")
+			s.error(s.offset, "illegal character NUL")
 		case r >= 0x80:
 			// not ASCII
-			r, w = utf8.DecodeRune(S.src[S.rdOffset:])
+			r, w = utf8.DecodeRune(s.src[s.rdOffset:])
 			if r == utf8.RuneError && w == 1 {
-				S.error(S.offset, "illegal UTF-8 encoding")
+				s.error(s.offset, "illegal UTF-8 encoding")
 			}
 		}
-		S.rdOffset += w
-		S.ch = r
+		s.rdOffset += w
+		s.ch = r
 	} else {
-		S.offset = len(S.src)
-		if S.ch == '\n' {
-			S.lineOffset = S.offset
-			S.file.AddLine(S.offset)
+		s.offset = len(s.src)
+		if s.ch == '\n' {
+			s.lineOffset = s.offset
+			s.file.AddLine(s.offset)
 		}
-		S.ch = -1 // eof
+		s.ch = -1 // eof
 	}
 }

Init() メソッドの変更:

--- a/src/pkg/go/scanner/scanner.go
+++ b/src/pkg/go/scanner/scanner.go
@@ -111,37 +111,37 @@ const (
 // Note that Init may call err if there is an error in the first character
 // of the file.
 //
-func (S *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode) {
+func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode) {
 	// Explicitly initialize all fields since a scanner may be reused.
 	if file.Size() != len(src) {
 		panic("file size does not match src len")
 	}
-	S.file = file
-	S.dir, _ = filepath.Split(file.Name())
-	S.src = src
-	S.err = err
-	S.mode = mode
+	s.file = file
+	s.dir, _ = filepath.Split(file.Name())
+	s.src = src
+	s.err = err
+	s.mode = mode
 
-	S.ch = ' '
-	S.offset = 0
-	S.rdOffset = 0
-	S.lineOffset = 0
-	S.insertSemi = false
-	S.ErrorCount = 0
+	s.ch = ' '
+	s.offset = 0
+	s.rdOffset = 0
+	s.lineOffset = 0
+	s.insertSemi = false
+	s.ErrorCount = 0
 
-	S.next()
+	s.next()
 }

コアとなるコードの解説

このコミットにおける「コアとなるコードの変更」は、Go言語のメソッド定義におけるレシーバ変数の命名規則の適用です。

Go言語では、メソッドを定義する際に、そのメソッドがどの型のインスタンスに対して動作するかを示すためにレシーバを指定します。レシーバは、通常の引数と同様に変数名を付けますが、Goの慣習では、その型を表す短い小文字の識別子を使用します。

例えば、Scanner型の場合、そのレシーバはsと命名するのが慣習的です。これは、コードの可読性を高め、メソッドがどの型に属しているかを一目で理解できるようにするためです。

この変更は、Scanner構造体のすべてのメソッド(next, Init, error, interpretLineComment, scanComment, findLineEnd, scanIdentifier, scanMantissa, scanNumber, scanEscape, scanChar, scanString, scanRawString, skipWhitespace, switch2, switch3, switch4, Scan)に適用されています。

これにより、go/scannerパッケージのコードベース全体が、Go言語の慣用的なスタイルガイドラインに準拠するようになり、Go言語の他の標準ライブラリや一般的なGoプロジェクトとの一貫性が向上します。これは、コードの保守性、共同作業のしやすさ、そしてGo言語の「Goらしさ」を維持する上で重要な変更です。

関連リンク

参考にした情報源リンク