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

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

このコミットは、Go言語の組み込み関数である append のドキュメントに、バイトスライスに文字列を追記する特殊なケースに関する記述を追加するものです。具体的には、[]byte 型のスライスに文字列を append する際の挙動が明示的に示されています。

コミット

commit f12796e9f7233266163d4003b528dd099b0f1799
Author: Rob Pike <r@golang.org>
Date:   Wed Feb 27 16:11:17 2013 -0800

    builtin: document appending a string to a byte slice
    Fixes #4873.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/7421043

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

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

元コミット内容

このコミットは、Go言語の組み込み関数 append のドキュメントに、バイトスライスに文字列を追記する際の特別なケースに関する説明を追加します。

具体的には、append([]byte("hello "), "world"...) のように、バイトスライスに文字列を可変長引数として ... を付けて渡すことで、文字列の内容をバイトスライスに追記できるという挙動が明記されました。

変更の背景

Go言語の append 関数は、スライスに要素を追加するための非常に汎用的な組み込み関数です。通常、append は同じ型の要素またはスライスを既存のスライスに追加するために使用されます。しかし、Go言語の設計において、文字列 (string) とバイトスライス ([]byte) は密接に関連しており、特に文字列がUTF-8エンコードされたバイト列として内部的に扱われるため、両者間には特定の変換規則が存在します。

このコミットが行われた背景には、append 関数がバイトスライスに文字列を追記できるという、一見すると直感的ではないが非常に便利な「特殊なケース」が存在し、それが公式ドキュメントに明記されていなかったという状況があります。開発者がこの挙動を理解し、適切に利用できるようにするため、ドキュメントの明確化が求められました。

Fixes #4873 とあることから、この挙動に関する既存のバグ報告や機能要望(Issue 4873)が存在し、その解決策としてドキュメントの追加が選択されたと考えられます。これにより、開発者はこの機能の存在を知り、誤解なく利用できるようになります。

前提知識の解説

Go言語のスライス ([]Type)

Go言語のスライスは、同じ型の要素の連続したシーケンスを参照するデータ構造です。スライスは配列の上に構築されており、長さ(len)と容量(cap)を持ちます。スライスは動的にサイズを変更できるかのように見えますが、実際には基になる配列の容量が許す範囲で拡張され、容量を超えると新しい基になる配列が割り当てられます。

Go言語の文字列 (string)

Go言語の文字列は、不変のバイトスライスです。Goの文字列はUTF-8エンコードされたテキストを表すことが推奨されており、内部的にはバイトのシーケンスとして扱われます。文字列は直接変更することはできません。

append 組み込み関数

append はGo言語の組み込み関数で、スライスに要素を追加するために使用されます。その基本的なシグネチャは以下の通りです。

func append(slice []Type, elems ...Type) []Type
  • slice: 要素を追加する対象のスライス。
  • elems ...Type: スライスに追加する要素。可変長引数として渡されます。これは、個々の要素を列挙することも、別のスライスを ... を付けて展開して渡すことも可能です。

append は新しいスライスを返します。これは、元のスライスの基になる配列に十分な容量がない場合、新しい配列が割り当てられ、要素がコピーされるためです。

文字列とバイトスライスの関係

Go言語では、文字列とバイトスライスは密接に関連しています。文字列は []byte に型変換することができ、その逆も可能です。

  • []byte("hello"): 文字列 "hello" をバイトスライスに変換します。
  • string([]byte{104, 101, 108, 108, 111}): バイトスライスを文字列に変換します。

このコミットで言及されている「特殊なケース」は、append 関数が []byte 型のスライスに対して、string 型の引数を ... を付けて受け入れることができるという点です。これは、Goコンパイラがこの特定のパターンを認識し、文字列をバイトスライスに変換してから append 操作を実行するためです。

技術的詳細

このコミットは、Go言語の仕様における append 関数の挙動、特に文字列とバイトスライスの間の暗黙的な変換に関する重要な側面を明確にしています。

Go言語の仕様では、append 関数の引数について厳密な型チェックが行われます。しかし、特定の状況下では、コンパイラが型変換を自動的に行うことがあります。このコミットで言及されているのは、まさにその一例です。

通常、append(slice []T, elems ...T) の形式では、elems の型は T と一致する必要があります。しかし、slice[]byte 型であり、elemsstring 型で ... を伴う場合、Goコンパイラは string[]byte に変換してから append 操作を実行します。これは、文字列が本質的にバイトのシーケンスであるというGo言語の設計思想に基づいています。

この挙動は、Go言語の仕様書(The Go Programming Language Specification)の「Built-in functions」セクションにある append 関数の説明に、この特殊なケースが明示的に記載されることで、公式に認められたものとなります。このドキュメントの追加は、Go言語の設計者がこの特定のユースケースを意図的にサポートしており、それが言語の自然な一部であることを示しています。

この機能は、文字列をバイトスライスに効率的に結合したい場合に非常に便利です。例えば、ネットワークプロトコルの構築やファイルI/Oなど、バイト列を扱う場面で、文字列リテラルや既存の文字列を直接バイトスライスに追記できるため、冗長な型変換コードを記述する必要がなくなります。

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

変更は src/pkg/builtin/builtin.go ファイルに対して行われました。

--- a/src/pkg/builtin/builtin.go
+++ b/src/pkg/builtin/builtin.go
@@ -114,6 +114,8 @@ type ComplexType complex64
 // result of append, often in the variable holding the slice itself:
 //	slice = append(slice, elem1, elem2)
 //	slice = append(slice, anotherSlice...)
+// As a special case, it is legal to append a string to a byte slice, like this:
+//	slice = append([]byte("hello "), "world"...)
 func append(slice []Type, elems ...Type) []Type
 
 // The copy built-in function copies elements from a source slice into a

具体的には、append 関数のドキュメントコメントに2行が追加されています。

コアとなるコードの解説

追加された2行は、append 関数の既存のドキュメントコメント内に挿入されています。

// As a special case, it is legal to append a string to a byte slice, like this:
//	slice = append([]byte("hello "), "world"...)
  • // As a special case, it is legal to append a string to a byte slice, like this: この行は、バイトスライスに文字列を追記することが「特殊なケース」として合法であることを明記しています。これは、通常の append の型規則からすると例外的な挙動であることを示唆しています。

  • // slice = append([]byte("hello "), "world"...) この行は、具体的なコード例を提供しています。[]byte("hello ") というバイトスライスに、文字列リテラル "world"... を付けて追記する例です。この ... は、文字列 "world" が個々のバイト要素に展開されて append 関数に渡されることを意味します。結果として、slice には "hello world" のバイト表現が格納されます。

この変更は、Go言語の組み込み関数のドキュメントをより完全で分かりやすいものにするためのものです。これにより、開発者は append 関数のこの特定の、しかし非常に便利な使用方法を公式に認識し、安心して利用できるようになります。

関連リンク

参考にした情報源リンク

  • Go Programming Language Specification - Built-in functions: https://go.dev/ref/spec#Built-in_functions (このコミットの後に更新された仕様書には、この特殊ケースが明記されています。)
  • Go Slices: usage and internals: https://go.dev/blog/slices
  • Go Strings, bytes, runes and characters: https://go.dev/blog/strings
  • Go issue #4873 (元のIssueは直接見つかりませんでしたが、コミットメッセージに記載されているため、このドキュメント追加の背景にある問題を示唆しています。)
    • (注: 検索結果ではGo言語のIssue #4873は直接特定できませんでしたが、コミットメッセージに記載されているため、このドキュメント追加の背景にある問題を示唆しています。)