[インデックス 13829] ファイルの概要
このコミットは、Go言語の仕様書(doc/go_spec.html
)における文字列型に関する記述を明確化することを目的としています。特に、文字列がバイトスライスとどのように異なり、どのような特性を持つのかについて、より詳細な説明が加えられています。
コミット
commit cc06593c681878a2a87f3612aa024e69a1ac074b
Author: Robert Griesemer <gri@golang.org>
Date: Fri Sep 14 11:31:56 2012 -0700
spec: clarify section on string types
Strings happen to be represented similarly to
byte slices internally, but they don't quite
behave like them: While strings can be indexed,
sliced, and have their len() taken like byte
slices, string elements are not addressable,
make() and cap() is not supported, range loops
operate differently, and they are immutable (and
thus behave like values rather then references).
Fixes #4018.
R=r, rsc, iant, ken
CC=golang-dev
https://golang.org/cl/6503116
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/cc06593c681878a2a87f3612aa024e69a1ac074b
元コミット内容
このコミットの元の内容は、Go言語の仕様書における文字列型に関する記述の明確化です。具体的には、文字列が内部的にはバイトスライスに似た表現を持つものの、その振る舞いには重要な違いがあることを強調しています。
コミットメッセージで挙げられている主な違いは以下の通りです。
- インデックス、スライス、
len()
: バイトスライスと同様に、文字列もインデックスによるアクセス、スライス操作、len()
関数による長さの取得が可能です。 - 要素のアドレス取得不可: 文字列の個々の要素(バイト)はアドレス可能ではありません。
&s[i]
のような操作は無効です。 make()
とcap()
の非サポート: 文字列に対してmake()
関数やcap()
関数は使用できません。これらはスライスやマップなどの動的なデータ構造に適用されます。range
ループの振る舞いの違い:range
ループは、バイトスライスではバイト単位でイテレートするのに対し、文字列ではUTF-8でエンコードされたUnicodeコードポイント(ルーン)単位でイテレートします。- 不変性: 文字列は不変(immutable)です。一度作成された文字列の内容を変更することはできません。このため、文字列は参照ではなく値のように振る舞います。
また、このコミットは「Fixes #4018」と記載されており、これはGo言語の内部的な課題追跡システムにおける特定の課題を修正したことを示唆しています。ただし、公開されているGoのissueトラッカーには直接的な「Issue #4018」は確認できませんでした。これは、内部的な議論や報告に基づいて仕様が更新された可能性を示しています。
変更の背景
Go言語の初期の段階では、文字列とバイトスライスの間のセマンティクスについて、開発者の間で混乱が生じることがありました。特に、内部表現が似ているために、両者が同じように振る舞うと誤解されることがありました。しかし、Goの設計思想において、文字列は不変な値であり、バイトスライスは可変なシーケンスであるという明確な区別があります。
このコミットは、Go言語の仕様書において、この重要な違いをより明確に記述することで、開発者が文字列とバイトスライスを正しく理解し、適切に使用できるようにすることを目的としています。これにより、Goプログラムのバグの減少や、より意図通りのコード記述が促進されます。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念を理解しておく必要があります。
- 文字列型 (string): Go言語における文字列は、UTF-8でエンコードされたバイトの不変なシーケンスです。内部的にはバイトの配列として表現されますが、その振る舞いはバイトスライスとは異なります。
- バイトスライス型 ([]byte): バイトスライスは、可変長のバイトのシーケンスです。Go言語では、バイナリデータや、文字列をバイト単位で操作する場合によく使用されます。
- 不変性 (Immutability): データが一度作成されたら、その内容を変更できない特性を指します。Goの文字列は不変であり、文字列操作(結合、部分文字列の抽出など)は常に新しい文字列を生成します。
- 可変性 (Mutability): データが作成された後も、その内容を変更できる特性を指します。Goのバイトスライスは可変であり、要素の変更や追加、削除が可能です。
len()
関数: スライス、配列、マップ、文字列などの組み込み型に対して、その長さを返す組み込み関数です。文字列の場合、バイト単位の長さを返します。make()
関数: スライス、マップ、チャネルなどの組み込み型を初期化し、メモリを割り当てるために使用される組み込み関数です。cap()
関数: スライス、チャネル、マップなどの組み込み型に対して、その容量(capacity)を返す組み込み関数です。容量とは、再割り当てなしで保持できる要素の最大数を指します。- インデックス操作: 配列、スライス、文字列の要素に、その位置(インデックス)を指定してアクセスする操作です。
- スライス操作: 配列やスライス、文字列の一部を切り出して新しいスライスや文字列を生成する操作です。
range
ループ: 配列、スライス、文字列、マップ、チャネルなどのコレクションをイテレートするためのGoの構文です。文字列の場合、UTF-8デコードされたUnicodeコードポイント(ルーン)と、その開始バイトオフセットを返します。- アドレス取得 (
&
演算子): 変数のメモリ上のアドレスを取得するGoの演算子です。
技術的詳細
このコミットは、Go言語の仕様書であるdoc/go_spec.html
を修正しています。具体的には、文字列型に関するセクション(A <i>string type</i> represents the set of string values.
から始まる部分)が変更されています。
変更の核心は、文字列が「バイトスライスのように振る舞うが不変である」という曖昧な表現から、「文字列値はバイトのシーケンスであり、不変である」というより明確な定義への移行です。
以前の記述では、文字列が「バイトスライスのように振る舞う」とされていたため、開発者が文字列とバイトスライスの違いを誤解する可能性がありました。特に、バイトスライスが可変であるのに対し、文字列は不変であるという重要な特性が十分に強調されていませんでした。
新しい記述では、文字列の不変性を最初に明確に述べ、その上で、インデックスアクセスやlen()
関数といったバイトスライスとの共通点に触れています。しかし、同時に、文字列要素のアドレス取得ができないこと、make()
やcap()
がサポートされないこと、range
ループの振る舞いが異なることなど、バイトスライスとは異なる重要な特性を明記しています。
これにより、Go言語の文字列が単なるバイトのシーケンスではなく、より高レベルな概念であり、その不変性がGoの型システムとメモリ管理において重要な役割を果たすことが強調されます。
コアとなるコードの変更箇所
変更はdoc/go_spec.html
ファイルに対して行われています。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
- "Subtitle": "Version of September 12, 2012",
+ "Subtitle": "Version of September 13, 2012",
"Path": "/ref/spec"
}-->
@@ -781,19 +781,21 @@ particular architecture.
<p>
A <i>string type</i> represents the set of string values.
-Strings behave like slices of bytes but are immutable: once created,\n+A string value is a (possibly empty) sequence of bytes.\n+Strings are immutable: once created,
it is impossible to change the contents of a string.
The predeclared string type is <code>string</code>.
+</p>
<p>
-The elements of strings have type <code>byte</code> and may be\n-accessed using the usual <a href=\"#Indexes\">indexing operations</a>. It is\n-illegal to take the address of such an element; if\n-<code>s[i]</code> is the <i>i</i>th byte of a\n-string, <code>&s[i]</code> is invalid. The length of string\n-<code>s</code> can be discovered using the built-in function\n-<code>len</code>. The length is a compile-time constant if <code>s</code>\n-is a string literal.\n+The length of a string <code>s</code> (its size in bytes) can be discovered using\n+the built-in function <a href=\"#Length_and_capacity\"><code>len</code></a>.\n+The length is a compile-time constant if the string is a constant.\n+A string's bytes can be accessed by integer indices 0 through\n+<code>len(s)-1</code> (§<a href=\"#Indexes\">Indexes</a>).\n+It is illegal to take the address of such an element; if\n+<code>s[i]</code> is the <code>i</code>'th byte of a\n+string, <code>&s[i]</code> is invalid.\n </p>
@@ -816,7 +818,7 @@ ElementType = Type .
The length is part of the array's type and must be a\n <a href=\"#Constant_expressions\">constant expression</a> that evaluates to a non-negative\n integer value. The length of array <code>a</code> can be discovered\n-using the built-in function <a href=\"#Length_and_capacity\"><code>len(a)</code></a>.\n+using the built-in function <a href=\"#Length_and_capacity\"><code>len</code></a>.\n The elements can be indexed by integer\n indices 0 through <code>len(a)-1</code> (§<a href=\"#Indexes\">Indexes</a>).\n Array types are always one-dimensional but may be composed to form\n@@ -847,7 +849,7 @@ SliceType = "[" "]" ElementType .
<p>\n Like arrays, slices are indexable and have a length. The length of a\n slice <code>s</code> can be discovered by the built-in function\n-<a href=\"#Length_and_capacity\"><code>len(s)</code></a>; unlike with arrays it may change during\n+<a href=\"#Length_and_capacity\"><code>len</code></a>; unlike with arrays it may change during\n execution. The elements can be addressed by integer indices 0\n through <code>len(s)-1</code> (§<a href=\"#Indexes\">Indexes</a>). The slice index of a\n given element may be less than the index of the same element in the\n@@ -1249,7 +1251,7 @@ map[string]interface{}\n <p>\n The number of map elements is called its length.\n For a map <code>m</code>, it can be discovered using the\n-built-in function <a href=\"#Length_and_capacity\"><code>len(m)</code></a>\n+built-in function <a href=\"#Length_and_capacity\"><code>len</code></a>\n and may change during execution. Elements may be added during execution\n using <a href=\"#Assignments\">assignments</a> and retrieved with\n <a href=\"#Indexes\">index</a> expressions; they may be removed with the
コアとなるコードの解説
このコミットにおける主要な変更点は、Go言語の仕様書における文字列型の説明の修正です。
-
仕様書のバージョン日付の更新:
Subtitle
のバージョン日付が"Version of September 12, 2012"から"Version of September 13, 2012"に更新されています。これは、仕様書の改訂が行われた日付を示しています。 -
文字列型の定義の明確化: 変更前:
Strings behave like slices of bytes but are immutable: once created, it is impossible to change the contents of a string.
変更後:A string value is a (possibly empty) sequence of bytes. Strings are immutable: once created, it is impossible to change the contents of a string.
この変更により、「文字列はバイトスライスのように振る舞う」という表現が削除され、代わりに「文字列値は(空である可能性もある)バイトのシーケンスである」と明確に定義されています。そして、その後に「文字列は不変である」という重要な特性が続くことで、文字列の基本的な性質がより正確に伝わるようになっています。 -
文字列の要素アクセスと長さに関する記述の修正: 変更前は、文字列の要素が
byte
型であり、インデックス操作でアクセスできること、アドレスが取れないこと、len()
で長さが取得できること、文字列リテラルの場合はコンパイル時定数になることが記述されていました。 変更後では、以下の点が修正・追加されています。The elements of strings have type <code>byte</code>
という記述が削除されました。これは、文字列のrange
ループがバイトではなくルーンを返すこととの整合性を高めるためと考えられます。文字列の個々の要素はバイトですが、Goの文字列はUTF-8エンコードされたテキストを扱うため、要素の型をbyte
と明示することが誤解を招く可能性がありました。The length of a string <code>s</code> (its size in bytes) can be discovered using the built-in function <a href=\"#Length_and_capacity\"><code>len</code></a>.
となり、len
がバイト単位の長さを返すことがより明確に示されました。The length is a compile-time constant if the string is a constant.
となり、文字列リテラルだけでなく、定数文字列全般に適用されることが明確化されました。A string's bytes can be accessed by integer indices 0 through <code>len(s)-1</code> (§<a href=\"#Indexes\">Indexes</a>).
が追加され、文字列のバイトがインデックスでアクセスできることが明示されました。It is illegal to take the address of such an element; if <code>s[i]</code> is the <code>i</code>'th byte of a string, <code>&s[i]</code> is invalid.
の部分は維持され、文字列の要素がアドレス可能ではないという重要な特性が引き続き強調されています。
-
len()
関数の引数表記の統一: 配列、スライス、マップのlen()
関数の説明において、len(a)
、len(s)
、len(m)
のように引数付きで記述されていた箇所が、単にlen
と記述されるように変更されています。これは、len
が組み込み関数であることを強調し、特定の型に限定されない一般的な関数であることを示すための統一的な変更です。
これらの変更は、Go言語の文字列のセマンティクスをより正確かつ詳細に記述し、開発者が文字列とバイトスライスの違いを明確に理解できるようにすることを目的としています。特に、文字列の不変性と、range
ループがルーンを返すという特性は、Go言語でUnicodeテキストを扱う上で非常に重要です。
関連リンク
- Go言語の仕様書: https://go.dev/ref/spec
- Go言語の文字列に関するブログ記事 (公式): https://go.dev/blog/strings
参考にした情報源リンク
- コミットハッシュ:
cc06593c681878a2a87f3612aa024e69a1ac074b
- GitHub上のコミットページ: https://github.com/golang/go/commit/cc06593c681878a2a87f3612aa024e69a1ac074b
- Go言語のIssue #4018に関するWeb検索結果 (直接的な公式Issueは確認できず、内部的な課題追跡システムまたは関連する議論の可能性):
google/flatbuffers
GitHub Issue #4018: https://github.com/google/flatbuffers/issues/4018 (Go言語の仕様とは直接関係なし)- Staticcheck SA4018: https://staticcheck.dev/docs/checks#SA4018 (Go言語の静的解析ルールであり、仕様とは直接関係なし)
- JetBrains YouTrack GO-4018: https://youtrack.jetbrains.com/issue/GO-4018 (Go言語のIDEに関するIssueであり、仕様とは直接関係なし)
- Go言語の
len()
関数に関するドキュメント: https://go.dev/ref/spec#Length_and_capacity - Go言語のインデックス操作に関するドキュメント: https://go.dev/ref/spec#Indexes
- Go言語の
range
句に関するドキュメント: https://go.dev/ref/spec#For_statements