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

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

このコミットは、Go言語の公式フォーマッタである gofmt-s オプションを付けて misc および src ディレクトリ内のコードに適用した結果を反映しています。主な変更点は、複合リテラル(Composite Literals)の簡略化であり、冗長な & 演算子の削除が行われています。これにより、コードの可読性と一貫性が向上しています。

コミット

commit dcf1d7bc0e6146c143454dc5077de9452e6fd795
Author: Russ Cox <rsc@golang.org>
Date:   Fri Dec 2 14:14:25 2011 -0500

    gofmt -s misc src

    R=golang-dev, bradfitz, gri
    CC=golang-dev
    https://golang.org/cl/5451079

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

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

元コミット内容

gofmt -s misc src

変更の背景

このコミットの背景には、Go言語のコードベース全体におけるコードスタイルの統一と可読性の向上が挙げられます。gofmt はGo言語のコードを自動的にフォーマットするツールであり、Goコミュニティでは gofmt によってフォーマットされたコードが標準とされています。-s オプションは、コードをより簡潔にするための追加の簡略化(simplification)を適用します。

具体的には、このコミットは複合リテラルにおける冗長な & 演算子の削除を目的としています。Go言語では、構造体や配列、スライスなどの複合リテラルを初期化する際に、そのリテラルのアドレスを取得するために & 演算子を使用することがあります。しかし、コンパイラが型を推論できる場合や、リテラルが直接ポインタ型に代入される場合など、& が冗長になるケースが存在します。gofmt -s はこのような冗長な & を自動的に削除し、コードをより簡潔に保ちます。

この変更は、Go言語の進化と、より洗練されたコードスタイルへの移行の一環として行われました。

前提知識の解説

gofmtgofmt -s

  • gofmt: Go言語のソースコードを標準的なスタイルに自動的にフォーマットするツールです。インデント、スペース、改行などを統一し、Goコミュニティ全体で一貫したコードスタイルを維持するのに役立ちます。これにより、異なる開発者によって書かれたコードでも、同じ見た目と構造を持つことが保証され、コードレビューや共同作業が容易になります。
  • gofmt -s: gofmt のオプションの一つで、コードの「簡略化 (simplify)」を行います。これは単なるフォーマット以上の変更であり、意味的に同じだがより簡潔な表現に書き換える機能です。例えば、今回のコミットで適用されている複合リテラルの & 削除などがこれに該当します。

Go言語の複合リテラル (Composite Literals)

Go言語では、構造体、配列、スライス、マップなどの複合型を初期化するために複合リテラルを使用します。

例:

type MyStruct struct {
    Field1 int
    Field2 string
}

// 構造体の複合リテラル
s := MyStruct{Field1: 10, Field2: "hello"}

// スライスの複合リテラル
arr := []int{1, 2, 3}

複合リテラルにおける & 演算子の簡略化

Go言語では、複合リテラルがポインタ型を期待するコンテキストで使用される場合、明示的に & 演算子を使ってリテラルのアドレスを取得する必要がありました。

例:

type Package struct {
    Name string
    Path string
}

// 以前の書き方: 明示的に & を使用
p := &Package{Name: "Go", Path: ""}

しかし、Goコンパイラは、複合リテラルがポインタ型に代入されることが明確な場合、& 演算子を省略してもそのリテラルのアドレスが取られることを推論できます。gofmt -s はこの最適化を適用し、冗長な & を削除します。

例:

// gofmt -s 適用後の書き方: & を省略
p := Package{Name: "Go", Path: ""} // この場合、p は *Package 型になる

この変更は、コードのセマンティクス(意味)を変えることなく、シンタックス(構文)を簡潔にするものです。

技術的詳細

このコミットで行われた変更は、Go言語のコードベース全体にわたる広範なリファクタリングです。gofmt -s ツールが実行され、Go言語の複合リテラルにおける冗長な & 演算子が削除されました。

具体的には、以下のようなパターンが変更されています。

  • &Type{...} -> {...}
    • これは、Type が構造体であり、その複合リテラルがポインタ型(*Type)を期待する変数に代入される場合に適用されます。コンパイラは、このコンテキストで & が必要であることを自動的に推論できるため、明示的な & は冗長と見なされます。
  • []*Type{&Type{...}, &Type{...}} -> []*Type{{...}, {...}}
    • スライスや配列の要素としてポインタ型の複合リテラルが使用される場合も同様です。各要素の & が削除されます。

この変更の技術的な利点は以下の通りです。

  1. コードの簡潔性向上: 冗長な記号が減ることで、コードがより読みやすくなります。特に、ネストされた複合リテラルや長い初期化リストにおいて、この簡略化は大きな効果を発揮します。
  2. 一貫性の強化: gofmt -s を通じて自動的に適用されるため、コードベース全体で複合リテラルの記述スタイルが一貫します。これにより、新しいコードを書く際にも迷いがなくなり、コードレビューの負担も軽減されます。
  3. パフォーマンスへの影響なし: この変更はコンパイル時に解決される構文上の簡略化であり、生成されるバイナリコードのパフォーマンスには影響を与えません。コンパイラは & が存在しない場合でも、必要に応じてアドレスを取得するコードを生成します。

このコミットは、Go言語の設計哲学である「シンプルさ」と「明瞭さ」を追求する典型的な例と言えます。ツールによる自動化されたリファクタリングは、大規模なコードベースの品質を維持する上で非常に有効な手段です。

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

このコミットは多数のファイルにわたる変更ですが、その性質は一貫しています。以下に代表的な変更箇所をいくつか示します。

misc/dashboard/app/build/test.go

--- a/misc/dashboard/app/build/test.go
+++ b/misc/dashboard/app/build/test.go
@@ -36,7 +36,7 @@ const testPkg = "code.google.com/p/go.more"
 var testPackage = &Package{Name: "Test", Path: testPkg}

 var testPackages = []*Package{
-	&Package{Name: "Go", Path: ""},
+	{Name: "Go", Path: ""},
 	testPackage,
 }

testPackages スライス内の &Package{...}{...} に変更されています。

src/cmd/cgo/out.go

--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -578,22 +578,22 @@ func c(repr string, args ...interface{}) *TypeRepr {

 // Map predeclared Go types to Type.
 var goTypes = map[string]*Type{
-	"int":        &Type{Size: 4, Align: 4, C: c("int")},
-	"uint":       &Type{Size: 4, Align: 4, C: c("uint")},
-	"int8":       &Type{Size: 1, Align: 1, C: c("schar")},
-	"uint8":      &Type{Size: 1, Align: 1, C: c("uchar")},
-	"int16":      &Type{Size: 2, Align: 2, C: c("short")},
-	"uint16":     &Type{Size: 2, Align: 2, C: c("ushort")},
-	"int32":      &Type{Size: 4, Align: 4, C: c("int")},
-	"uint32":     &Type{Size: 4, Align: 4, C: c("uint")},
-	"int64":      &Type{Size: 8, Align: 8, C: c("int64")},
-	"uint64":     &Type{Size: 8, Align: 8, C: c("uint64")},
-	"float":      &Type{Size: 4, Align: 4, C: c("float")},
-	"float32":    &Type{Size: 4, Align: 4, C: c("float")},
-	"float64":    &Type{Size: 8, Align: 8, C: c("double")},
-	"complex":    &Type{Size: 8, Align: 8, C: c("__complex float")},
-	"complex64":  &Type{Size: 8, Align: 8, C: c("__complex float")},
-	"complex128": &Type{Size: 16, Align: 16, C: c("__complex double")},
+	"int":        {Size: 4, Align: 4, C: c("int")},
+	"uint":       {Size: 4, Align: 4, C: c("uint")},
+	"int8":       {Size: 1, Align: 1, C: c("schar")},
+	"uint8":      {Size: 1, Align: 1, C: c("uchar")},
+	"int16":      {Size: 2, Align: 2, C: c("short")},
+	"uint16":     {Size: 2, Align: 2, C: c("ushort")},
+	"int32":      {Size: 4, Align: 4, C: c("int")},
+	"uint32":     {Size: 4, Align: 4, C: c("uint")},
+	"int64":      {Size: 8, Align: 8, C: c("int64")},
+	"uint64":     {Size: 8, Align: 8, C: c("uint64")},
+	"float":      {Size: 4, Align: 4, C: c("float")},
+	"float32":    {Size: 4, Align: 4, C: c("float")},
+	"float64":    {Size: 8, Align: 8, C: c("double")},
+	"complex":    {Size: 8, Align: 8, C: c("__complex float")},
+	"complex64":  {Size: 8, Align: 8, C: c("__complex float")},
+	"complex128": {Size: 16, Align: 16, C: c("__complex double")},
 }

goTypes マップの初期化において、&Type{...}{...} に変更されています。

src/pkg/archive/tar/reader_test.go

--- a/src/pkg/archive/tar/reader_test.go
+++ b/src/pkg/archive/tar/reader_test.go
@@ -24,7 +24,7 @@ type untarTest struct {
 var gnuTarTest = &untarTest{
 	file: "testdata/gnu.tar",
 	headers: []*Header{
-		&Header{
+		{
 			Name:     "small.txt",
 			Mode:     0640,
 			Uid:      73025,
@@ -35,7 +35,7 @@ var gnuTarTest = &untarTest{
 			Uname:    "dsymonds",
 			Gname:    "eng",
 		},
-		&Header{
+		{
 			Name:     "small2.txt",
 			Mode:     0640,
 			Uid:      73025,
@@ -55,10 +55,10 @@ var gnuTarTest = &untarTest{

 var untarTests = []*untarTest{
 	gnuTarTest,
-	&untarTest{
+	{
 		file: "testdata/star.tar",
 		headers: []*Header{
-			&Header{
+			{
 				Name:       "small.txt",
 				Mode:       0640,
 				Uid:        73025,
@@ -71,7 +71,7 @@ var untarTests = []*untarTest{
 				AccessTime: time.Unix(1244592783, 0),
 				ChangeTime: time.Unix(1244592783, 0),
 			},
-			&Header{
+			{
 				Name:       "small2.txt",
 				Mode:       0640,
 				Uid:        73025,
@@ -86,10 +86,10 @@ var untarTests = []*untarTest{
 			},
 		},
 	},
-	&untarTest{
+	{
 		file: "testdata/v7.tar",
 		headers: []*Header{
-			&Header{
+			{
 				Name:     "small.txt",
 				Mode:     0444,
 				Uid:      73025,
@@ -98,7 +98,7 @@ var untarTests = []*untarTest{
 				ModTime:  time.Unix(1244593104, 0),
 				Typeflag: '\x00',
 			},
-			&Header{
+			{
 				Name:     "small2.txt",
 				Mode:     0444,
 				Uid:      73025,

テストデータ内の &Header{...}&untarTest{...}{...} に変更されています。

コアとなるコードの解説

これらの変更は、Go言語の複合リテラルがポインタ型を生成する際に、明示的な & 演算子が不要になるケースを gofmt -s が自動的に検出して削除したものです。

例えば、misc/dashboard/app/build/test.go の変更では、testPackages という []*Package 型のスライスを初期化しています。このスライスは *Package 型の要素を期待しているため、Package{Name: "Go", Path: ""} という複合リテラルは自動的に *Package 型の値(つまり、Package 構造体のアドレス)として扱われます。したがって、& を明示的に記述する必要がなくなります。

同様に、src/cmd/cgo/out.gogoTypes マップの初期化や、src/pkg/archive/tar/reader_test.go のテストデータ初期化においても、複合リテラルがポインタ型を期待するコンテキストで使用されているため、& が省略されています。

この簡略化は、コードの見た目をすっきりとさせ、冗長性を排除することで、Go言語のコードがより「Goらしい」スタイルに近づくことを意味します。開発者は、ポインタが必要な場合に常に & を書くのではなく、コンパイラが型を推論できる場合は省略できるというGo言語の柔軟性を享受できます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • gofmt のマニュアルページ
  • Go言語の複合リテラルに関する一般的な知識
  • Go言語のポインタに関する一般的な知識
  • GitHub上のGo言語リポジトリのコミット履歴