[インデックス 14807] ファイルの概要
このコミットは、Go言語の標準ライブラリbytes
パッケージ内のドキュメントとテストコードにおいて、「array」という用語が不適切に使用されていた箇所を「slice」に修正するものです。Go言語における「array(配列)」と「slice(スライス)」は異なる概念であり、bytes
パッケージの多くの関数はバイトスライスを操作するため、ドキュメントの正確性を向上させる目的で行われました。
コミット
commit c75340488616b2744cbd7029d4b5a8e7003eaf87
Author: Nigel Tao <nigeltao@golang.org>
Date: Mon Jan 7 10:48:06 2013 +1100
bytes: fix docs: s/array/slice/.
R=rsc, mdempsky, dave
CC=golang-dev
https://golang.org/cl/7028051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c75340488616b2744cbd7029d4b5a8e7003eaf87
元コミット内容
bytes: fix docs: s/array/slice/.
R=rsc, mdempsky, dave
CC=golang-dev
https://golang.org/cl/7028051
変更の背景
Go言語では、固定長で値型の「配列(array)」と、可変長で参照型の「スライス(slice)」という、2つの異なるデータ構造が存在します。多くのGoの関数やメソッドは、柔軟性や効率性の観点から、配列ではなくスライスを引数として受け取り、または戻り値として返します。
bytes
パッケージは、バイト列([]byte
)を操作するためのユーティリティ関数を提供しており、これらの関数はほとんどの場合、バイトスライスを扱います。しかし、既存のドキュメントやコメント、一部のテストコードにおいて、本来「スライス」と記述すべき箇所が誤って「配列」と記述されていました。
このコミットの背景には、Go言語のドキュメントの正確性を高め、開発者がGoのデータ構造について誤解するのを防ぐという目的があります。特に、Goの初心者にとっては、配列とスライスの違いは混乱しやすいポイントであるため、公式ドキュメントでの正確な用語使用は非常に重要です。
前提知識の解説
Go言語における配列 (Array)
Go言語の配列は、同じ型の要素を固定された数だけ格納するデータ構造です。配列の長さは型の一部であり、コンパイル時に決定されます。
例: var a [5]int
は5つの整数を格納できる配列を宣言します。
特徴:
- 固定長: 一度宣言すると長さを変更できません。
- 値型: 配列を別の変数に代入したり、関数に渡したりすると、配列全体のコピーが作成されます。
- 宣言時に長さが必要:
[5]int
のように長さを指定します。
Go言語におけるスライス (Slice)
Go言語のスライスは、配列のセグメント(一部)を参照するデータ構造です。スライスは、基となる配列へのポインタ、長さ(len
)、容量(cap
)の3つの要素で構成されます。スライスは動的に長さを変更できるため、Goプログラミングにおいて配列よりも頻繁に使用されます。
例: var s []int
は整数のスライスを宣言します。make([]int, 5)
は長さ5、容量5の整数スライスを作成します。
特徴:
- 可変長:
append
関数などを使って要素を追加することで、長さを動的に変更できます(必要に応じて基となる配列が再割り当てされる)。 - 参照型: スライスを別の変数に代入したり、関数に渡したりしても、基となる配列のコピーは作成されず、同じ基となる配列を参照します。これにより、メモリ効率が向上します。
- 宣言時に長さは不要:
[]int
のように長さを指定せずに宣言できます。
なぜ「array」から「slice」への修正が重要なのか
bytes
パッケージの関数は、[]byte
型を引数として受け取ります。この[]byte
はバイトスライスであり、バイト配列ではありません。例えば、bytes.Compare
関数は2つのバイトスライスを比較します。もしドキュメントが「バイト配列を比較する」と記述していれば、読者は固定長のバイト配列を渡す必要があると誤解する可能性があります。しかし実際には、可変長のバイトスライスを渡すのが一般的であり、Goのイディオムに沿っています。
この修正は、Go言語のコアなデータ構造に関する正確な理解を促進し、コードの意図をより明確に伝えるために不可欠です。
技術的詳細
このコミットは、主にsrc/pkg/bytes/bytes.go
内の関数シグネチャのコメント、およびsrc/pkg/bytes/buffer_test.go
とsrc/pkg/bytes/bytes_test.go
内のテストコードのコメントや変数名における「array」という単語を「slice」に置換しています。
具体的な変更は以下の通りです。
-
コメントの修正:
bytes.Compare
関数のドキュメント: 「Compare returns an integer comparing the two byte arrays lexicographically.」から「Compare returns an integer comparing two byte slices lexicographically.」へ変更。bytes.explode
関数のドキュメント: 「explode splits s into an array of UTF-8 sequences... up to a maximum of n byte arrays.」から「explode splits s into a slice of UTF-8 sequences... up to a maximum of n byte slices.」へ変更。bytes.genSplit
関数のドキュメント: 「including sepSave bytes of sep in the subarrays.」から「including sepSave bytes of sep in the subslices.」へ変更。bytes.Fields
関数のドキュメント: 「Fields splits the array s around each instance... returning a slice of subarrays of s...」から「Fields splits the slice s around each instance... returning a slice of subslices of s...」へ変更。bytes.FieldsFunc
関数のドキュメント: 「It splits the array s at each run of code points... returns a slice of subarrays of s.」から「It splits the slice s at each run of code points... returns a slice of subslices of s.」へ変更。bytes.Join
関数のドキュメント: 「Join concatenates the elements of a to create a new byte array. The separator sep is placed between elements in the resulting array.」から「Join concatenates the elements of s to create a new byte slice. The separator sep is placed between elements in the resulting slice.」へ変更。bytes.HasPrefix
,bytes.HasSuffix
,bytes.Map
,bytes.ToUpper
,bytes.ToLower
,bytes.ToTitle
,bytes.ToUpperSpecial
,bytes.ToLowerSpecial
,bytes.ToTitleSpecial
関数のドキュメント: いずれも「byte array s」を「byte slice s」に修正。
-
テストコード内のコメントと変数名の修正:
src/pkg/bytes/buffer_test.go
のTestRuneIO
関数内のコメント: 「Built a test array while we write the data」から「Built a test slice while we write the data」へ変更。src/pkg/bytes/bytes_test.go
内のヘルパー関数名:arrayOfString
をsliceOfString
にリネーム。これに伴い、この関数を呼び出している箇所もarrayOfString
からsliceOfString
に修正。src/pkg/bytes/bytes_test.go
内のコメント: 「For ease of reading, the test cases use strings that are converted to byte arrays before invoking the functions.」から「For ease of reading, the test cases use strings that are converted to byte slices before invoking the functions.」へ変更。src/pkg/bytes/bytes_test.go
内のコメント: 「Test case for any function which accepts and returns a byte array. For ease of creation, we write the byte arrays as strings.」から「Test case for any function which accepts and returns a byte slice. For ease of creation, we write the byte slices as strings.」へ変更。
これらの変更は、コードの動作には影響を与えませんが、ドキュメントとコードの整合性を高め、Go言語のイディオムに沿った表現に統一することを目的としています。
コアとなるコードの変更箇所
このコミットは、主にコメントとテストコード内の文字列置換が中心であり、Go言語のランタイムやロジックに直接的な変更はありません。以下に、src/pkg/bytes/bytes.go
における主要なコメント変更と、src/pkg/bytes/bytes_test.go
における関数名変更の例を示します。
src/pkg/bytes/bytes.go
の変更例
--- a/src/pkg/bytes/bytes.go
+++ b/src/pkg/bytes/bytes.go
@@ -11,7 +11,7 @@ import (
"unicode/utf8"
)
-// Compare returns an integer comparing the two byte arrays lexicographically.
+// Compare returns an integer comparing two byte slices lexicographically.
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b
// A nil argument is equivalent to an empty slice.
func Compare(a, b []byte) int {
@@ -53,8 +53,8 @@ func equalPortable(a, b []byte) bool {
return true
}
-// explode splits s into an array of UTF-8 sequences, one per Unicode character (still arrays of bytes),
-// up to a maximum of n byte arrays. Invalid UTF-8 sequences are chopped into individual bytes.
+// explode splits s into a slice of UTF-8 sequences, one per Unicode character (still slices of bytes),
+// up to a maximum of n byte slices. Invalid UTF-8 sequences are chopped into individual bytes.
func explode(s []byte, n int) [][]byte {
if n <= 0 {
n = len(s)
@@ -226,7 +226,7 @@ func LastIndexAny(s []byte, chars string) int {
}
// Generic split: splits after each instance of sep,
-// including sepSave bytes of sep in the subarrays.
+// including sepSave bytes of sep in the subslices.
func genSplit(s, sep []byte, sepSave, n int) [][]byte {
if n == 0 {
return nil
@@ -287,15 +287,15 @@ func SplitAfter(s, sep []byte) [][]byte {
return genSplit(s, sep, len(sep), -1)
}
-// Fields splits the array s around each instance of one or more consecutive white space
-// characters, returning a slice of subarrays of s or an empty list if s contains only white space.
+// Fields splits the slice s around each instance of one or more consecutive white space
+// characters, returning a slice of subslices of s or an empty list if s contains only white space.
func Fields(s []byte) [][]byte {
return FieldsFunc(s, unicode.IsSpace)
}
// FieldsFunc interprets s as a sequence of UTF-8-encoded Unicode code points.
-// It splits the array s at each run of code points c satisfying f(c) and
-// returns a slice of subarrays of s. If no code points in s satisfy f(c), an
+// It splits the slice s at each run of code points c satisfying f(c) and
+// returns a slice of subslices of s. If no code points in s satisfy f(c), an
// empty slice is returned.
func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
n := 0
@@ -333,46 +333,46 @@ func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
return a[0:na]
}
-// Join concatenates the elements of a to create a new byte array. The separator
-// sep is placed between elements in the resulting array.
-func Join(a [][]byte, sep []byte) []byte {
- if len(a) == 0 {
+// Join concatenates the elements of s to create a new byte slice. The separator
+// sep is placed between elements in the resulting slice.
+func Join(s [][]byte, sep []byte) []byte {
+ if len(s) == 0 {
return []byte{}
}
- if len(a) == 1 {
+ if len(s) == 1 {
// Just return a copy.
- return append([]byte(nil), a[0]...)
+ return append([]byte(nil), s[0]...)
}
- n := len(sep) * (len(a) - 1)
- for i := 0; i < len(a); i++ {
- n += len(a[i])
+ n := len(sep) * (len(s) - 1)
+ for _, v := range s {
+ n += len(v)
}
b := make([]byte, n)
- bp := copy(b, a[0])
- for _, s := range a[1:] {
+ bp := copy(b, s[0])
+ for _, v := range s[1:] {
bp += copy(b[bp:], sep)
- bp += copy(b[bp:], s)
+ bp += copy(b[bp:], v)
}
return b
}
-// HasPrefix tests whether the byte array s begins with prefix.
+// HasPrefix tests whether the byte slice s begins with prefix.
func HasPrefix(s, prefix []byte) bool {
return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix)
}
-// HasSuffix tests whether the byte array s ends with suffix.
+// HasSuffix tests whether the byte slice s ends with suffix.
func HasSuffix(s, suffix []byte) bool {
return len(s) >= len(suffix) && Equal(s[len(s)-len(suffix):], suffix)
}
-// Map returns a copy of the byte array s with all its characters modified
+// Map returns a copy of the byte slice s with all its characters modified
// according to the mapping function. If mapping returns a negative value, the character is
// dropped from the string with no replacement. The characters in s and the
// output are interpreted as UTF-8-encoded Unicode code points.
func Map(mapping func(r rune) rune, s []byte) []byte {
- // In the worst case, the array can grow when mapped, making
+ // In the worst case, the slice can grow when mapped, making
// things unpleasant. But it's so rare we barge in assuming it's
// fine. It could also shrink but that falls out naturally.
maxbytes := len(s) // length of b
@@ -413,28 +413,28 @@ func Repeat(b []byte, count int) []byte {
return nb
}
-// ToUpper returns a copy of the byte array s with all Unicode letters mapped to their upper case.
+// ToUpper returns a copy of the byte slice s with all Unicode letters mapped to their upper case.
func ToUpper(s []byte) []byte { return Map(unicode.ToUpper, s) }
-// ToLower returns a copy of the byte array s with all Unicode letters mapped to their lower case.
+// ToLower returns a copy of the byte slice s with all Unicode letters mapped to their lower case.
func ToLower(s []byte) []byte { return Map(unicode.ToLower, s) }
-// ToTitle returns a copy of the byte array s with all Unicode letters mapped to their title case.
+// ToTitle returns a copy of the byte slice s with all Unicode letters mapped to their title case.
func ToTitle(s []byte) []byte { return Map(unicode.ToTitle, s) }
-// ToUpperSpecial returns a copy of the byte array s with all Unicode letters mapped to their
+// ToUpperSpecial returns a copy of the byte slice s with all Unicode letters mapped to their
// upper case, giving priority to the special casing rules.
func ToUpperSpecial(_case unicode.SpecialCase, s []byte) []byte {
return Map(func(r rune) rune { return _case.ToUpper(r) }, s)
}
-// ToLowerSpecial returns a copy of the byte array s with all Unicode letters mapped to their
+// ToLowerSpecial returns a copy of the byte slice s with all Unicode letters mapped to their
// lower case, giving priority to the special casing rules.
func ToLowerSpecial(_case unicode.SpecialCase, s []byte) []byte {
return Map(func(r rune) rune { return _case.ToLower(r) }, s)
}
-// ToTitleSpecial returns a copy of the byte array s with all Unicode letters mapped to their
+// ToTitleSpecial returns a copy of the byte slice s with all Unicode letters mapped to their
// title case, giving priority to the special casing rules.
func ToTitleSpecial(_case unicode.SpecialCase, s []byte) []byte {
return Map(func(r rune) rune { return _case.ToTitle(r) }, s)
src/pkg/bytes/bytes_test.go
の変更例
--- a/src/pkg/bytes/bytes_test.go
+++ b/src/pkg/bytes/bytes_test.go
@@ -25,16 +25,16 @@ func eq(a, b []string) bool {
return true
}
-func arrayOfString(a [][]byte) []string {
- result := make([]string, len(a))
- for j := 0; j < len(a); j++ {
- result[j] = string(a[j])
+func sliceOfString(s [][]byte) []string {
+ result := make([]string, len(s))
+ for i, v := range s {
+ result[i] = string(v)
}
return result
}
// For ease of reading, the test cases use strings that are converted to byte
-// arrays before invoking the functions.
+// slices before invoking the functions.
var abcd = "abcd"
var faces = "☺☻☹"
@@ -435,7 +435,7 @@ var explodetests = []ExplodeTest{
func TestExplode(t *testing.T) {
for _, tt := range explodetests {
a := SplitN([]byte(tt.s), nil, tt.n)
- result := arrayOfString(a)
+ result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf(`Explode("%s", %d) = %v; want %v`, tt.s, tt.n, result, tt.a)
continue
@@ -473,7 +473,7 @@ var splittests = []SplitTest{
func TestSplit(t *testing.T) {
for _, tt := range splittests {
a := SplitN([]byte(tt.s), []byte(tt.sep), tt.n)
- result := arrayOfString(a)
+ result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a)
continue
@@ -519,7 +519,7 @@ var splitaftertests = []SplitTest{
func TestSplitAfter(t *testing.T) {
for _, tt := range splitaftertests {
a := SplitAfterN([]byte(tt.s), []byte(tt.sep), tt.n)
- result := arrayOfString(a)
+ result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a)
continue
@@ -559,7 +559,7 @@ var fieldstests = []FieldsTest{
func TestFields(t *testing.T) {
for _, tt := range fieldstests {
a := Fields([]byte(tt.s))
- result := arrayOfString(a)
+ result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf("Fields(%q) = %v; want %v", tt.s, a, tt.a)
continue
@@ -570,7 +570,7 @@ func TestFields(t *testing.T) {
func TestFieldsFunc(t *testing.T) {
for _, tt := range fieldstests {
a := FieldsFunc([]byte(tt.s), unicode.IsSpace)
- result := arrayOfString(a)
+ result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf("FieldsFunc(%q, unicode.IsSpace) = %v; want %v", tt.s, a, tt.a)
continue
@@ -585,15 +585,15 @@ func TestFieldsFunc(t *testing.T) {
}
for _, tt := range fieldsFuncTests {
a := FieldsFunc([]byte(tt.s), pred)
- result := arrayOfString(a)
+ result := sliceOfString(a)
if !eq(result, tt.a) {
t.Errorf("FieldsFunc(%q) = %v, want %v", tt.s, a, tt.a)
}
}
}
-// Test case for any function which accepts and returns a byte array.
-// For ease of creation, we write the byte arrays as strings.
+// Test case for any function which accepts and returns a byte slice.
+// For ease of creation, we write the byte slices as strings.
type StringTest struct {
in, out string
}
コアとなるコードの解説
このコミットにおける「コアとなるコード」は、Go言語のbytes
パッケージ内の関数定義そのものではなく、それらの関数が操作するデータ型に関するドキュメントとテストコードの記述です。
Go言語では、[]byte
はバイトスライスであり、バイト配列ではありません。しかし、初期のドキュメントやコードでは、この区別が曖昧であったり、一般的なプログラミング言語の「配列」という概念に引きずられて「array」という用語が使われていたりする箇所がありました。
このコミットの目的は、Go言語の設計思想と実際の型定義に即して、より正確な用語である「slice」を使用することです。
- コメントの修正: 各関数の説明文において、「byte array」という表現を「byte slice」に置き換えることで、その関数がバイトスライスを操作するものであることを明確にしています。これにより、開発者は関数の挙動や期待される引数の型について、より正確な情報を得ることができます。
- テストコード内の修正:
arrayOfString
からsliceOfString
への関数名変更は、その関数がバイトスライスのスライス([][]byte
)を文字列のスライス([]string
)に変換するヘルパー関数であることを、名前から直感的に理解できるようにするためです。以前のarrayOfString
という名前は、Goの配列とスライスの区別を曖昧にする可能性がありました。- テストケースのコメントも同様に、「byte arrays」を「byte slices」に修正することで、テストデータがバイトスライスとして扱われていることを明示しています。
これらの変更は、Go言語のコードベース全体の品質と一貫性を向上させるための、細部へのこだわりを示しています。特に、Goの学習者や、他の言語からGoに移行してきた開発者にとって、このような用語の正確性は、Goのユニークなデータ構造を正しく理解する上で非常に役立ちます。
関連リンク
- Go Slices: usage and internals - The Go Programming Language - Go言語のスライスに関する公式ブログ記事。スライスの内部構造と使用法について詳しく解説されています。
- Effective Go - Slices - The Go Programming Language - Go言語の効率的なプログラミングに関するガイド。スライスの効果的な利用方法が説明されています。
参考にした情報源リンク
- https://golang.org/cl/7028051 - このコミットに対応するGoのコードレビューシステム(Gerrit)のチェンジリスト。コミットの詳細な変更内容やレビューのやり取りを確認できます。