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

[インデックス 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点です。

  1. デコードエラー時の戻り値の明確化: DecodeRuneDecodeRuneInStringなどの関数が不正なUTF-8シーケンスを検出した場合に、どのような値を返すのかが明示されていませんでした。Goの慣習として、エラーケースの挙動はドキュメントで明確に記述されるべきであり、これにより開発者がこれらの関数をより安全かつ正確に使用できるようになります。特に、RuneErrorと幅1バイトという組み合わせが不正なUTF-8シーケンスを示す「不可能な結果」であることを明記することで、エラーハンドリングの指針を提供しています。
  2. 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を提供しています。runeint32のエイリアスであり、単一のUnicodeコードポイントを表します。

例えば、Goのコードで文字リテラルを記述すると、それはrune型になります。

var r rune = '世' // '世'のUnicodeコードポイントはU+4E16
fmt.Printf("%T %d\n", r, r) // 出力: int32 19990

stringrangeループでイテレートすると、各要素は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パッケージ内の特定のデコード関数のドキュメンテーションの更新に集約されます。

変更前は、DecodeRuneDecodeRuneInStringDecodeLastRuneDecodeLastRuneInStringといった関数が不正なUTF-8シーケンスを処理した場合の戻り値について、明示的な記述がありませんでした。これらの関数は、内部的に不正なシーケンスを検出するとutf8.RuneErrorを返し、そのシーケンスが占めるバイト幅を1バイトとして報告します。

このコミットでは、各関数のドキュメンテーションコメントに以下の記述が追加されました。

// If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.

この記述は非常に重要です。

  1. RuneErrorの返却: 不正なエンコーディングの場合、関数はutf8.RuneErrorを返します。これは、その位置に有効なUnicode文字が存在しないことを示します。
  2. 幅が1バイト: 不正なシーケンスであっても、関数は常に何らかのバイトを消費して処理を進める必要があります。UTF-8の最小単位は1バイトであるため、不正なシーケンスの最初の1バイトを消費し、その幅を1と報告します。
  3. 「正しい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 {

コアとなるコードの解説

このコミットによるコードの変更は、すべてドキュメンテーションコメントの修正です。実際の関数のロジックには一切変更がありません。

  1. 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の役割をより正確に表現しています。
  2. DecodeRune 関数のコメントの追加:

    • // If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
    • この行が追加され、DecodeRuneが不正なUTF-8シーケンスを検出した場合の戻り値(RuneErrorと幅1バイト)が明示されました。これにより、開発者はこの戻り値の組み合わせがエラーを示すことを確実に理解し、適切なエラーハンドリングを実装できます。
  3. DecodeRuneInString 関数のコメントの追加:

    • // If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
    • DecodeRuneの文字列版であるこの関数にも同様のコメントが追加され、一貫したドキュメンテーションが提供されています。
  4. 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. が追加されました。これは、文字列の末尾からデコードする場合でも、不正なシーケンスに対する戻り値の挙動が明確であることを示しています。
  5. DecodeLastRuneInString 関数のコメントの追加:

    • // If the encoding is invalid, it returns (RuneError, 1), an impossible result for correct UTF-8.
    • DecodeLastRuneの文字列版であるこの関数にも同様のコメントが追加され、すべてのデコード関数でエラー時の挙動が統一的にドキュメント化されました。

これらの変更は、コードの動作自体を変えるものではなく、Goの標準ライブラリのドキュメンテーションの品質と正確性を向上させるためのものです。これにより、ライブラリの利用者がより簡単に、かつ誤解なく関数を使用できるようになります。

関連リンク

参考にした情報源リンク