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

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

このコミットは、Go言語の公式仕様書である doc/go_spec.html ファイルに対する変更です。このファイルは、Go言語の構文、セマンティクス、組み込み型、関数などを詳細に記述したHTMLドキュメントであり、Go言語の設計と動作を理解するための最も権威ある情報源の一つです。

コミット

commit 72a2979ef07e309f1168ed5a5e144ceeddb25472
Author: David Symonds <dsymonds@golang.org>
Date:   Tue Nov 29 15:47:36 2011 -0800

    spec: update spacing to match gofmt, where reasonable.
    
    R=gri, rsc
    CC=golang-dev
    https://golang.org/cl/5327053

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

https://github.com/golang/go/commit/72a2979ef07e309f1168ed5a5e144ceeddb25472

元コミット内容

このコミットは、Go言語の仕様書 (doc/go_spec.html) におけるコード例の空白(スペース)の記述を、Go言語の公式フォーマッタである gofmt の出力に合わせることを目的としています。コミットメッセージには「spec: update spacing to match gofmt, where reasonable.」とあり、これは「仕様書:合理的な範囲でgofmtに合うようにスペースを更新する」という意味です。レビュー担当者として gri (Robert Griesemer) と rsc (Russ Cox) が、CCとして golang-dev メーリングリストが指定されています。また、GoのコードレビューシステムであるGerritの変更リストへのリンク (https://golang.org/cl/5327053) も含まれています。

変更の背景

Go言語は、コードの可読性と一貫性を非常に重視しています。その中心的なツールが gofmt です。gofmt は、Goのソースコードを自動的に整形し、Goコミュニティ全体で統一されたコーディングスタイルを強制します。これにより、異なる開発者が書いたコードでも、見た目が一貫し、レビューや理解が容易になります。

このコミットが行われた背景には、Go言語の仕様書に記載されているコード例が、実際の gofmt の出力と異なる空白のルールを持っていたという問題があります。仕様書は言語の「真実」を記述するものであり、その中に示されるコード例は、言語の推奨されるスタイルを反映しているべきです。もし仕様書の例と gofmt の出力が異なると、開発者はどちらを信じるべきか混乱し、Go言語のコードスタイルの一貫性が損なわれる可能性があります。

したがって、この変更は、Go言語の仕様書と gofmt の出力との間の不整合を解消し、Go言語のコードスタイルに関する一貫性と権威を強化することを目的としています。これにより、開発者は仕様書を読みながら、同時に gofmt が適用する正しいコードスタイルを学ぶことができます。

前提知識の解説

Go言語の仕様書 (Go Language Specification)

Go言語の仕様書は、Go言語の構文、セマンティクス、メモリモデル、組み込み関数、パッケージなど、言語のあらゆる側面を定義する公式ドキュメントです。これはGo言語の「憲法」のようなものであり、Goコンパイラやツール、そして開発者がGoコードを書く際の最終的な参照点となります。doc/go_spec.html は、この仕様書のHTML版です。

gofmt

gofmt は、Go言語のソースコードを自動的に整形するツールです。GoのSDKに標準で含まれており、Go開発者にとって不可欠なツールの一つです。gofmt の主な目的は以下の通りです。

  • 一貫性の確保: チームやプロジェクト全体で統一されたコーディングスタイルを強制し、コードの可読性を向上させます。
  • 議論の削減: コードスタイルの好みに関する議論を不要にし、開発者がより本質的な問題に集中できるようにします。
  • ツールの統合: gofmt が生成する標準的なフォーマットは、他のGoツール(リンター、アナライザーなど)との統合を容易にします。

gofmt は、スペース、インデント、改行、括弧の位置など、多くのフォーマットルールを自動的に適用します。そのルールは非常に厳格であり、Goコミュニティでは gofmt の出力に従うことが強く推奨されています。

コードの可読性と一貫性

ソフトウェア開発において、コードの可読性と一貫性は非常に重要です。

  • 可読性: コードが読みやすいと、他の開発者(または未来の自分)がコードの意図を素早く理解できます。これにより、バグの発見が容易になり、機能追加や変更の効率が向上します。
  • 一貫性: プロジェクト内のコードが一貫したスタイルで書かれていると、新しいコードベースに慣れる時間が短縮され、コードレビューがスムーズになります。また、特定のスタイルに慣れることで、コードのパターンを認識しやすくなります。

gofmt は、これらの目標を達成するためのGo言語の主要なメカニズムです。

技術的詳細

このコミットは、doc/go_spec.html ファイル内の複数のコード例において、主に以下の種類の空白の調整を行っています。

  1. 構造体リテラルと型定義:

    • struct { a, b int } のような構造体定義で、struct{ の間のスペースが削除され、struct{ a, b int } のように変更されています。これは gofmt が構造体リテラルや型定義で採用するコンパクトなスタイルに一致します。
    • 匿名フィールドの宣言 T*T の後のスペースが調整されています。
  2. マップ型定義:

    • map [string] int のようなマップ型定義で、map[ の間のスペース、および ] と型の間のスペースが削除され、map[string]int のように変更されています。ポインタ型を含む場合も同様に *map[string] *chan int*map[string]*chan int に変更されています。
  3. make 関数呼び出し:

    • make(map[string] int) のような make 関数呼び出しで、マップ型引数内のスペースが削除され、make(map[string]int) のように変更されています。
  4. チャネル型定義:

    • chan Tchan<- float64<-chan int のようなチャネル型定義で、型とコメントの間のスペースが調整され、より一貫したインデントが適用されています。特に、chan<- chan int のようなネストされたチャネル型では、chan<- chan intchan<- chan int のようにスペースが調整されています。
  5. 定数宣言:

    • const zero = 0.0eof = -1 のような定数宣言で、= の周りのスペースが調整され、値とコメントの間のスペースも一貫性が保たれるように変更されています。特に、複数の定数をまとめて宣言するブロック内で、= の位置が揃うように調整されています。
  6. 変数宣言:

    • var i int のような変数宣言で、変数名と型の間のスペースが調整されています。
  7. 配列リテラル:

    • [10]string{} のような配列リテラルで、[] と型の間のスペースが削除され、[10]string{} のように変更されています。
  8. セレクタ式:

    • p.zp.M2 のようなセレクタ式で、セレクタとコメントの間のスペースが調整されています。
  9. スライス式:

    • a[2:]a[:3] のようなスライス式で、スライスとコメントの間のスペースが調整されています。
  10. 関数呼び出し:

    • math.Atan2(x, y) のような関数呼び出しで、引数リストとコメントの間のスペースが調整されています。
  11. 型変換:

    • string('a')string(-1) のような型変換で、変換対象とコメントの間のスペースが調整されています。
  12. 代入文:

    • i, x[i] = 1, 2 のような多重代入文で、= の周りのスペースが調整されています。

これらの変更は、主に視覚的な一貫性を高めるためのものであり、Go言語のセマンティクスや実行時の動作に影響を与えるものではありません。しかし、仕様書が示すコード例が gofmt の出力と一致することで、Go言語のコードスタイルに関する混乱が解消され、開発体験が向上します。

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

このコミットは、doc/go_spec.html ファイルの以下の行に影響を与えています。

  • 構造体型定義の例 (L912-914):
    -	T         // conflicts with anonymous field *T and *P.T
    -	*T        // conflicts with anonymous field T and *P.T
    -	*P.T      // conflicts with anonymous field T and *T
    +	T     // conflicts with anonymous field *T and *P.T
    +	*T    // conflicts with anonymous field T and *P.T
    +	*P.T  // conflicts with anonymous field T and *T
    
  • マップ型定義の例 (L974):
    -*map[string] *chan int
    +*map[string]*chan int
    
  • マップ型定義の例 (L1153-1155):
    -map [string] int
    -map [*T] struct { x, y float64 }
    -map [string] interface {}
    +map[string]int
    +map[*T]struct{ x, y float64 }
    +map[string]interface{}
    
  • make 関数呼び出しの例 (L1174-1175):
    -make(map[string] int)
    -make(map[string] int, 100)
    +make(map[string]int)
    +make(map[string]int, 100)
    
  • チャネル型定義の例 (L1207-1209):
    -chan T         // can be used to send and receive values of type T
    -chan<- float64 // can only be used to send float64s
    -<-chan int     // can only be used to receive ints
    +chan T          // can be used to send and receive values of type T
    +chan<- float64  // can only be used to send float64s
    +<-chan int      // can only be used to receive ints
    
  • ネストされたチャネル型定義の例 (L1218-1220):
    -chan<- chan int     // same as chan<- (chan int)
    -chan<- <-chan int   // same as chan<- (<-chan int)
    -<-chan <-chan int   // same as <-chan (<-chan int)
    +chan<- chan int    // same as chan<- (chan int)
    +chan<- <-chan int  // same as chan<- (<-chan int)
    +<-chan <-chan int  // same as <-chan (<-chan int)
    
  • 構造体型定義の例 (L1306-1307):
    -	T2 struct { a, b int }
    -	T3 struct { a, c int }
    +	T2 struct{ a, b int }
    +	T3 struct{ a, c int }
    
  • 型同一性の例 (L1320):
    -struct { a, b *T5 } and struct { a, b *T5 }
    +struct{ a, b *T5 } and struct{ a, b *T5 }
    
  • 定数宣言の例 (L1564-1567):
    -const zero = 0.0             // untyped floating-point constant
    +const zero = 0.0         // untyped floating-point constant
    const (
    	size int64 = 1024
    -	eof = -1             // untyped integer constant
    +	eof        = -1  // untyped integer constant
    )
    
  • iota を使用した定数宣言の例 (L1639-1642):
    -	bit0, mask0 = 1 << iota, 1 << iota - 1  // bit0 == 1, mask0 == 0
    -	bit1, mask1                             // bit1 == 2, mask1 == 1
    -	_, _                                    // skips iota == 2
    -	bit3, mask3                             // bit3 == 8, mask3 == 7
    +	bit0, mask0 = 1 << iota, 1<<iota - 1  // bit0 == 1, mask0 == 0
    +	bit1, mask1                           // bit1 == 2, mask1 == 1
    +	_, _                                  // skips iota == 2
    +	bit3, mask3                           // bit3 == 8, mask3 == 7
    
  • 構造体型定義の例 (L1670):
    -	Point struct { x, y float64 }
    +	Point struct{ x, y float64 }
    
  • 変数宣言の例 (L1753):
    -	i int
    +	i       int
    
  • 配列リテラルの例 (L2090-2092):
    -buffer := [10]string{}               // len(buffer) == 10
    -intSet := [6]int{1, 2, 3, 5}         // len(intSet) == 6
    -days := [...]string{"Sat", "Sun"}    // len(days) == 2
    +buffer := [10]string{}             // len(buffer) == 10
    +intSet := [6]int{1, 2, 3, 5}       // len(intSet) == 6
    +days := [...]string{"Sat", "Sun"}  // len(days) == 2
    
  • セレクタ式の例 (L2331-2336):
    -p.z         // (*p).z
    -p.y         // ((*p).T1).y
    -p.x         // (*(*p).T0).x
    +p.z   // (*p).z
    +p.y   // ((*p).T1).y
    +p.x   // (*(*p).T0).x
    
    -p.M2        // (*p).M2
    -p.M1        // ((*p).T1).M1
    -p.M0        // ((*p).T0).M0
    +p.M2  // (*p).M2
    +p.M1  // ((*p).T1).M1
    +p.M0  // ((*p).T0).M0
    
  • スライス式の例 (L2476-2478):
    -a[2:]	// same a[2 : len(a)]
    -a[:3]   // same as a[0 : 3]
    -a[:]    // same as a[0 : len(a)]
    +a[2:]  // same a[2 : len(a)]
    +a[:3]  // same as a[0 : 3]
    +a[:]   // same as a[0 : len(a)]
    
  • 関数呼び出しの例 (L2571):
    -math.Atan2(x, y)    // function call
    +math.Atan2(x, y)  // function call
    
  • メソッド宣言の例 (L3091):
    -func (tv  T) Mv(a int)     int     { return 0 }  // value receiver
    +func (tv  T) Mv(a int) int         { return 0 }  // value receiver
    
  • 型変換の例 (L3337-3341):
    -string('a')           // "a"
    -string(-1)            // "\ufffd" == "\xef\xbf\xbd "
    -string(0xf8)          // "\u00f8" == "ø" == "\xc3\xb8"
    +string('a')       // "a"
    +string(-1)        // "\ufffd" == "\xef\xbf\xbd "
    +string(0xf8)      // "\u00f8" == "ø" == "\xc3\xb8"
    type MyString string
    -MyString(0x65e5)      // "\u65e5" == "日" == "\xe6\x97\xa5"
    +MyString(0x65e5)  // "\u65e5" == "日" == "\xe6\x97\xa5"
    
  • バイトスライスから文字列への型変換の例 (L3351):
    -string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'})   // "hellø"
    +string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'})  // "hellø"
    
  • runeスライスから文字列への型変換の例 (L3365):
    -string([]rune{0x767d, 0x9d6c, 0x7fd4})   // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    +string([]rune{0x767d, 0x9d6c, 0x7fd4})  // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    
  • 文字列からバイトスライスへの型変換の例 (L3378-3379):
    -[]byte("hellø")  // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    -MyBytes("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    +[]byte("hellø")   // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    +MyBytes("hellø")  // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    
  • 定数変換の不正な例 (L3473-3477):
    -uint(-1)       // -1 cannot be represented as a uint
    -int(3.14)      // 3.14 cannot be represented as an int
    -int64(Huge)    // 1<<100 cannot be represented as an int64
    -Four * 300     // 300 cannot be represented as an int8
    -Four * 100     // 400 cannot be represented as an int8
    +uint(-1)     // -1 cannot be represented as a uint
    +int(3.14)    // 3.14 cannot be represented as an int
    +int64(Huge)  // 1<<100 cannot be represented as an int64
    +Four * 300   // 300 cannot be represented as an int8
    +Four * 100   // 400 cannot be represented as an int8
    
  • ビット演算の例 (L3487-3491):
    -^1          // untyped integer constant, equal to -2
    -uint8(^1)   // error, same as uint8(-2), out of range
    -^uint8(1)   // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
    -int8(^1)    // same as int8(-2)
    -^int8(1)    // same as -1 ^ int8(1) = -2
    +^1         // untyped integer constant, equal to -2
    +uint8(^1)  // error, same as uint8(-2), out of range
    +^uint8(1)  // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
    +int8(^1)   // same as int8(-2)
    +^int8(1)   // same as -1 ^ int8(1) = -2
    
  • 評価順序の例 (L3517):
    -y[f()], ok = g(h(), i() + x[j()], <-c), k()
    +y[f()], ok = g(h(), i()+x[j()], <-c), k()
    
  • 多重代入の例 (L3732-3739):
    -i, x[i] = 1, 2   // set i = 1, x[0] = 2
    +i, x[i] = 1, 2  // set i = 1, x[0] = 2
    
    i = 0
    x[i], i = 2, 1  // set x[0] = 2, i = 1
    
    x[0], x[0] = 1, 2  // set x[0] = 1, then x[0] = 2 (so x[0] = 2 at end)
    
    -x[1], x[3] = 4, 5 // set x[1] = 4, then panic setting x[3] = 5.
    +x[1], x[3] = 4, 5  // set x[1] = 4, then panic setting x[3] = 5.
    
  • select 文の例 (L4247):
    -select { }  // block forever
    +select {}  // block forever
    
  • make 関数呼び出しの例 (L4644-4647):
    -s := make([]int, 10, 100)        // slice with len(s) == 10, cap(s) == 100
    -s := make([]int, 10)             // slice with len(s) == cap(s) == 10
    -c := make(chan int, 10)          // channel with a buffer size of 10
    -m := make(map[string] int, 100)  // map with initial space for 100 elements
    +s := make([]int, 10, 100)       // slice with len(s) == 10, cap(s) == 100
    +s := make([]int, 10)            // slice with len(s) == cap(s) == 10
    +c := make(chan int, 10)         // channel with a buffer size of 10
    +m := make(map[string]int, 100)  // map with initial space for 100 elements
    
  • append 関数呼び出しの例 (L4693):
    -b = append(b, "bar"...)    // append string contents      b == []byte{'b', 'a', 'r' }
    +b = append(b, "bar"...)  // append string contents      b == []byte{'b', 'a', 'r' }
    
  • for range ループの例 (L5008):
    -	for i := range src {\t// Loop over values received from 'src'.
    +	for i := range src {  // Loop over values received from 'src'.
    

コアとなるコードの解説

このコミットのコアとなる変更は、Go言語の仕様書に記載されているコード例の空白を、gofmt が生成する標準的なフォーマットに合わせることにあります。具体的には、以下のようなパターンでスペースが調整されています。

  • 型とブラケット/波括弧の間のスペースの削除:

    • map [string] int -> map[string]int
    • struct { x, y float64 } -> struct{ x, y float64 }
    • [10]string{} -> [10]string{} これは、Goの型システムにおける複合型(マップ、構造体、配列)の宣言において、型名とそれに続くブラケットや波括弧の間にスペースを入れないという gofmt のルールを反映しています。これにより、型定義がよりコンパクトで読みやすくなります。
  • 演算子や区切り文字の周りのスペースの調整:

    • *map[string] *chan int -> *map[string]*chan int
    • i() + x[j()] -> i()+x[j()]
    • 1 << iota - 1 -> 1<<iota - 1 ポインタ型を示す * や、算術演算子、ビットシフト演算子などの周りのスペースが、gofmt の規則に従って調整されています。特に、* が型の一部として使われる場合(例: *T)と、ポインタのデリファレンスとして使われる場合(例: *p)で、スペースの扱いが異なることがあります。gofmt は、これらの文脈を区別し、一貫したフォーマットを適用します。
  • コメントのインデントと配置の調整:

    • コード行の末尾に付加されるコメント(//)の開始位置が、その行のコードの長さに応じて調整され、縦方向で揃うように変更されています。これにより、コードとコメントの間の視覚的な区切りが明確になり、可読性が向上します。
  • = 演算子の位置揃え:

    • 複数の変数や定数をまとめて宣言するブロックにおいて、= 演算子の位置が縦方向に揃うようにスペースが追加または削除されています。これは、コードのブロック全体を一覧した際の可読性を高めるための一般的なコーディングスタイルです。

これらの変更は、Go言語のコードが「Goらしい」見た目になるように、細部にわたって調整されたものです。gofmt は、このような細かなルールを自動的に適用することで、Goコミュニティ全体で統一されたコードスタイルを維持し、開発者がコードの整形ではなく、本質的なロジックに集中できるように貢献しています。仕様書がこの gofmt のスタイルに追従することで、Go言語の学習者や開発者は、公式ドキュメントを通じて正しいコーディングスタイルを自然に習得できるようになります。

関連リンク

参考にした情報源リンク

  • gofmt の概要: https://go.dev/blog/gofmt
  • Web search results for "gofmt Go language" (この解説の作成時に参照した情報源)