[インデックス 11761] ファイルの概要
このコミットは、Go言語のunicode/utf8
パッケージにおけるドキュメンテーションの改善と、rune
の定義に関する記述の更新を目的としています。具体的には、UTF-8デコードエラー時の戻り値に関する説明が追加され、パッケージのコメントにおけるrune
の記述がより現代的で正確なものに修正されています。
コミット
commit 6c0aa2f296142c7acbd3b99e0378fe39b76db48a
Author: Rob Pike <r@golang.org>
Date: Fri Feb 10 14:12:17 2012 +1100
unicode/utf8: document return value for decode errors
Also replace archaic definition of rune.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5654048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6c0aa2f296142c7acbd3b99e0378fe39b76db48a
元コミット内容
unicode/utf8: document return value for decode errors
Also replace archaic definition of rune.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5654048
変更の背景
このコミットは、Go言語のunicode/utf8
パッケージのドキュメンテーションの明確性を向上させるために行われました。主な背景は以下の2点です。
- デコードエラー時の戻り値の明確化:
DecodeRune
やDecodeRuneInString
などの関数が不正なUTF-8シーケンスを検出した場合に、どのような値を返すのかが明示されていませんでした。Goの慣習として、エラーケースの挙動はドキュメントで明確に記述されるべきであり、これにより開発者がこれらの関数をより安全かつ正確に使用できるようになります。特に、RuneError
と幅1バイトという組み合わせが不正なUTF-8シーケンスを示す「不可能な結果」であることを明記することで、エラーハンドリングの指針を提供しています。 rune
の定義の更新:unicode/utf8
パッケージの冒頭のコメントで、rune
が「簡潔さのためにUnicode文字を指す」と説明されていました。しかし、Go言語におけるrune
型は実際にはUnicodeコードポイントを表すint32
のエイリアスであり、単なる「簡潔さのための呼び名」というよりは、Go言語の文字・文字列処理における中心的な概念です。この記述をより正確で現代的なものに更新することで、Go言語の設計思想とrune
の役割に対する理解を深めることを意図しています。
これらの変更は、Go言語の標準ライブラリの品質と使いやすさを継続的に改善する取り組みの一環です。
前提知識の解説
このコミットを理解するためには、以下の概念について基本的な知識が必要です。
UTF-8
UTF-8(Unicode Transformation Format - 8-bit)は、Unicode文字を可変長バイトシーケンスでエンコードするための文字エンコーディング方式です。ASCII文字は1バイトで表現され、それ以外の文字は2バイトから4バイトで表現されます。UTF-8の大きな特徴は、ASCII互換性があり、多言語を効率的に表現できる点です。Go言語の文字列は内部的にUTF-8でエンコードされています。
Unicodeとコードポイント
Unicodeは、世界中のあらゆる文字を統一的に扱うための文字コード標準です。各文字には一意の「コードポイント」と呼ばれる数値が割り当てられています。例えば、'A'
のコードポイントはU+0041、'あ'
のコードポイントはU+3042です。
Go言語におけるrune
Go言語では、string
型はUTF-8バイトの不変シーケンスとして扱われます。個々のUnicodeコードポイントを扱うために、Goは組み込み型rune
を提供しています。rune
はint32
のエイリアスであり、単一のUnicodeコードポイントを表します。
例えば、Goのコードで文字リテラルを記述すると、それはrune
型になります。
var r rune = '世' // '世'のUnicodeコードポイントはU+4E16
fmt.Printf("%T %d\n", r, r) // 出力: int32 19990
string
をrange
ループでイテレートすると、各要素はrune
と、そのrune
が文字列内で占めるバイト数(UTF-8エンコードされたバイト数)になります。
unicode/utf8
パッケージ
Goの標準ライブラリであるunicode/utf8
パッケージは、UTF-8エンコードされたバイトシーケンスとrune
(Unicodeコードポイント)の間で変換を行うための関数を提供します。主な機能には、UTF-8シーケンスのデコード(バイト列からrune
とバイト幅を取得)、エンコード(rune
からバイト列を生成)、UTF-8シーケンスの検証などがあります。
utf8.RuneError
utf8.RuneError
は、unicode/utf8
パッケージで定義されている定数で、不正なUnicodeコードポイントやUTF-8シーケンスを表すために使用されます。その値はU+FFFD
(REPLACEMENT CHARACTER)です。これは、文字コード変換中に不正なバイトシーケンスが検出された場合などに、その不正なシーケンスの代わりに挿入される特殊な文字です。
DecodeRune
のような関数が不正なUTF-8シーケンスをデコードしようとした場合、有効なrune
を返すことができないため、RuneError
を返します。
技術的詳細
このコミットの技術的詳細は、unicode/utf8
パッケージ内の特定のデコード関数のドキュメンテーションの更新に集約されます。
変更前は、DecodeRune
、DecodeRuneInString
、DecodeLastRune
、DecodeLastRuneInString
といった関数が不正なUTF-8シーケンスを処理した場合の戻り値について、明示的な記述がありませんでした。これらの関数は、内部的に不正なシーケンスを検出するとutf8.RuneError
を返し、そのシーケンスが占めるバイト幅を1バイトとして報告します。
このコミットでは、各関数のドキュメンテーションコメントに以下の記述が追加されました。
// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
この記述は非常に重要です。
RuneError
の返却: 不正なエンコーディングの場合、関数はutf8.RuneError
を返します。これは、その位置に有効なUnicode文字が存在しないことを示します。- 幅が1バイト: 不正なシーケンスであっても、関数は常に何らかのバイトを消費して処理を進める必要があります。UTF-8の最小単位は1バイトであるため、不正なシーケンスの最初の1バイトを消費し、その幅を1と報告します。
- 「正しいUTF-8では不可能な結果」: この部分が最も重要です。正しいUTF-8エンコーディングにおいて、
RuneError
(U+FFFD)がエンコードされた場合のバイト幅は3バイトです(0xEF 0xBF 0xBD
)。したがって、RuneError
が返され、かつその幅が1バイトであるという組み合わせは、有効なUTF-8シーケンスからは決して発生しないことを意味します。この特性を利用することで、開発者は関数の戻り値を見て、デコードが成功したのか、それとも入力が不正なUTF-8シーケンスであったのかを確実に判別できるようになります。
例えば、DecodeRune
が(r, size)
を返した場合、r == utf8.RuneError && size == 1
であれば、入力バイト列が不正なUTF-8シーケンスであったと判断できます。それ以外の場合(r != utf8.RuneError
、またはr == utf8.RuneError
だがsize == 3
の場合)は、有効なrune
がデコードされたと解釈できます。
また、パッケージの冒頭のコメントの変更も技術的な意味合いを持ちます。
変更前: // This package calls a Unicode character a rune for brevity.
変更後: // UTF-8. It includes functions to translate between runes and UTF-8 byte sequences.
この変更は、rune
が単なる「簡潔さのための呼び名」ではなく、UTF-8バイトシーケンスとUnicodeコードポイント間の変換を扱う上で中心的な概念であることを強調しています。Go言語の設計においてrune
が果たす役割をより正確に反映した記述となっています。
コアとなるコードの変更箇所
--- a/src/pkg/unicode/utf8/utf8.go
+++ b/src/pkg/unicode/utf8/utf8.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// Package utf8 implements functions and constants to support text encoded in
-// UTF-8. This package calls a Unicode character a rune for brevity.
+// UTF-8. It includes functions to translate between runes and UTF-8 byte sequences.
package utf8
import "unicode" // only needed for a couple of constants
@@ -198,19 +198,21 @@ func FullRuneInString(s string) bool {
}
// DecodeRune unpacks the first UTF-8 encoding in p and returns the rune and its width in bytes.
+// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
func DecodeRune(p []byte) (r rune, size int) {
r, size, _ = decodeRuneInternal(p)
return
}
// DecodeRuneInString is like DecodeRune but its input is a string.
+// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
func DecodeRuneInString(s string) (r rune, size int) {
r, size, _ = decodeRuneInStringInternal(s)
return
}
-// DecodeLastRune unpacks the last UTF-8 encoding in p
-// and returns the rune and its width in bytes.
+// DecodeLastRune unpacks the last UTF-8 encoding in p and returns the rune and its width in bytes.
+// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
func DecodeLastRune(p []byte) (r rune, size int) {
end := len(p)
if end == 0 {
@@ -244,6 +246,7 @@ func DecodeLastRune(p []byte) (r rune, size int) {
}
// DecodeLastRuneInString is like DecodeLastRune but its input is a string.
+// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
func DecodeLastRuneInString(s string) (r rune, size int) {
end := len(s)
if end == 0 {
コアとなるコードの解説
このコミットによるコードの変更は、すべてドキュメンテーションコメントの修正です。実際の関数のロジックには一切変更がありません。
-
src/pkg/unicode/utf8/utf8.go
のパッケージコメントの変更:- 変更前:
// UTF-8. This package calls a Unicode character a rune for brevity.
- 変更後:
// UTF-8. It includes functions to translate between runes and UTF-8 byte sequences.
- この変更は、
rune
が単なる「簡潔さのための呼び名」ではなく、UTF-8とrune
間の変換機能を提供するパッケージの核心的な要素であることを明確にしています。Go言語におけるrune
の役割をより正確に表現しています。
- 変更前:
-
DecodeRune
関数のコメントの追加:// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
- この行が追加され、
DecodeRune
が不正なUTF-8シーケンスを検出した場合の戻り値(RuneError
と幅1バイト)が明示されました。これにより、開発者はこの戻り値の組み合わせがエラーを示すことを確実に理解し、適切なエラーハンドリングを実装できます。
-
DecodeRuneInString
関数のコメントの追加:// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
DecodeRune
の文字列版であるこの関数にも同様のコメントが追加され、一貫したドキュメンテーションが提供されています。
-
DecodeLastRune
関数のコメントの修正と追加:- 元のコメントが改行されていましたが、
// DecodeLastRune unpacks the last UTF-8 encoding in p and returns the rune and its width in bytes.
と一行にまとめられました。 - さらに、
// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
が追加されました。これは、文字列の末尾からデコードする場合でも、不正なシーケンスに対する戻り値の挙動が明確であることを示しています。
- 元のコメントが改行されていましたが、
-
DecodeLastRuneInString
関数のコメントの追加:// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
DecodeLastRune
の文字列版であるこの関数にも同様のコメントが追加され、すべてのデコード関数でエラー時の挙動が統一的にドキュメント化されました。
これらの変更は、コードの動作自体を変えるものではなく、Goの標準ライブラリのドキュメンテーションの品質と正確性を向上させるためのものです。これにより、ライブラリの利用者がより簡単に、かつ誤解なく関数を使用できるようになります。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/
unicode/utf8
パッケージのドキュメント: https://pkg.go.dev/unicode/utf8- Go言語における
rune
の概念に関するブログ記事(公式ブログなど):- Go Slices: usage and internals - The Go Programming Language (直接
rune
に特化した記事ではないが、Goのデータ構造の基本を理解する上で役立つ) - Strings, bytes, runes, and characters in Go - The Go Programming Language (Goにおける文字列、バイト、rune、文字に関する詳細な解説)
- Go Slices: usage and internals - The Go Programming Language (直接
参考にした情報源リンク
- GitHub上のコミットページ: https://github.com/golang/go/commit/6c0aa2f296142c7acbd3b99e0378fe39b76db48a
- Gerrit Code Review (Goプロジェクトのコードレビューシステム): https://golang.org/cl/5654048 (コミットメッセージに記載されている変更リストのリンク)
- UTF-8に関するWikipedia記事: https://ja.wikipedia.org/wiki/UTF-8
- Unicodeに関するWikipedia記事: https://ja.wikipedia.org/wiki/Unicode
- Go言語の
rune
型に関する情報(Go言語の仕様書など): https://go.dev/ref/spec#Rune_literals U+FFFD REPLACEMENT CHARACTER
に関する情報: https://www.fileformat.info/info/unicode/char/fffd/index.htm