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

[インデックス 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>&amp;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>&amp;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言語の仕様書における文字列型の説明の修正です。

  1. 仕様書のバージョン日付の更新: Subtitleのバージョン日付が"Version of September 12, 2012"から"Version of September 13, 2012"に更新されています。これは、仕様書の改訂が行われた日付を示しています。

  2. 文字列型の定義の明確化: 変更前: 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. この変更により、「文字列はバイトスライスのように振る舞う」という表現が削除され、代わりに「文字列値は(空である可能性もある)バイトのシーケンスである」と明確に定義されています。そして、その後に「文字列は不変である」という重要な特性が続くことで、文字列の基本的な性質がより正確に伝わるようになっています。

  3. 文字列の要素アクセスと長さに関する記述の修正: 変更前は、文字列の要素が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>&amp;s[i]</code> is invalid. の部分は維持され、文字列の要素がアドレス可能ではないという重要な特性が引き続き強調されています。
  4. len()関数の引数表記の統一: 配列、スライス、マップのlen()関数の説明において、len(a)len(s)len(m)のように引数付きで記述されていた箇所が、単にlenと記述されるように変更されています。これは、lenが組み込み関数であることを強調し、特定の型に限定されない一般的な関数であることを示すための統一的な変更です。

これらの変更は、Go言語の文字列のセマンティクスをより正確かつ詳細に記述し、開発者が文字列とバイトスライスの違いを明確に理解できるようにすることを目的としています。特に、文字列の不変性と、rangeループがルーンを返すという特性は、Go言語でUnicodeテキストを扱う上で非常に重要です。

関連リンク

参考にした情報源リンク