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

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

このコミットは、Go言語の仕様書 doc/go_spec.html の変更に関するものです。具体的には、GoソースコードにおけるUTF-8エンコードされたバイトオーダーマーク(BOM)の扱いに関する記述が修正されています。

コミット

commit 488350ac423094a3cd252696c8e841168f1705a8
Author: Rob Pike <r@golang.org>
Date:   Fri Sep 7 10:28:24 2012 -0700

    spec: an initial BOM can be ignored
    After further deliberation, let's back down to the Unicode proposal.
    Ignoring aBOMinations anywhere means that things like
            grep unsafe *.go
    might fail because there's a BOM in the middle: unBOMsafe.
    
    R=golang-dev, rsc, 0xjnml, gri, bradfitz
    CC=golang-dev
    https://golang.org/cl/6490091

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

https://github.com/golang/go/commit/488350ac423094a3cd252696c8e841168f1705a8

元コミット内容

このコミットの元の内容は、Go言語の仕様書において、UTF-8エンコードされたバイトオーダーマーク(BOM)の扱いに関する記述を修正することです。当初はソーステキスト中の「どこにでも」存在するBOMを無視する可能性を示唆していましたが、その後の検討により、ファイルの「最初の」UnicodeコードポイントとしてのみBOMを無視するという、より厳格な方針に変更されました。これは、BOMがファイルの中間に存在する場合に、grepのようなツールが意図しない動作をする可能性を避けるためです。

変更の背景

この変更の背景には、Go言語のソースコードの可読性とツールとの互換性の確保という重要な考慮事項があります。

当初のGo言語の仕様では、UTF-8エンコードされたバイトオーダーマーク(BOM、U+FEFF)がソーステキスト中の文字列リテラルやルーンリテラル以外であれば、コンパイラが「どこにでも」無視できる可能性を示唆していました。しかし、この「どこにでも」無視するという方針は、以下のような問題を引き起こす可能性がありました。

  1. ツールの互換性問題: grepのようなテキスト検索ツールは、BOMを通常の文字として扱います。もしBOMがファイルの中間に挿入された場合、例えば grep unsafe *.go のようなコマンドが、実際には unsafe という文字列が含まれているにもかかわらず、BOMによって unBOMsafe のように認識され、期待通りにマッチしないという問題が発生します。これは、開発者が日常的に使用するツールとの間で予期せぬ不整合を生み出し、デバッグやコード検索の効率を著しく低下させます。
  2. コードの曖昧さ: BOMは、UTF-8においてはバイトオーダーを示す必要がないため、通常は不要なものです。ファイルの中間にBOMが存在することは、意図しない文字の挿入や、エディタの設定ミスなど、潜在的な問題を示唆する可能性があります。Go言語はシンプルさと明確さを重視しており、このような曖昧さは避けるべきであると考えられました。
  3. Unicode標準への準拠: Unicodeの勧告では、UTF-8におけるBOMはファイルの先頭にのみ存在し、エンコーディングの識別子として使用されることが一般的です。ファイルの中間にBOMが存在することは、この慣例から逸脱しており、Go言語の設計哲学である「実用性」と「シンプルさ」に反すると判断されました。

これらの理由から、Goチームは「さらなる検討」の結果、Unicodeの一般的な慣例に立ち返り、BOMはソーステキストの「最初の」Unicodeコードポイントとしてのみ無視されるべきであるという結論に至りました。これにより、Goソースコードの予測可能性が高まり、既存のテキスト処理ツールとの互換性が向上し、より堅牢な開発環境が提供されることになります。

前提知識の解説

バイトオーダーマーク (BOM)

バイトオーダーマーク(Byte Order Mark, BOM)は、Unicodeテキストファイルの先頭に挿入される特殊なUnicode文字(U+FEFF)です。主にUTF-16やUTF-32のようなマルチバイトエンコーディングにおいて、バイトの並び順(エンディアン、ビッグエンディアンまたはリトルエンディアン)を示すために使用されます。

  • UTF-8におけるBOM: UTF-8はバイトオーダーに依存しないエンコーディングであるため、技術的にはBOMは不要です。しかし、一部のテキストエディタやシステム(特にWindows環境)では、UTF-8ファイルをBOM付きで保存することがあります。これは、そのファイルがUTF-8でエンコードされていることを明示的に示す目的で使用されます。
    • BOM付きUTF-8ファイルの先頭バイト列は EF BB BF となります。
  • BOMの問題点:
    • 互換性: BOMを認識しないツールやプログラミング言語では、BOMが通常の文字として扱われ、予期せぬエラーや動作を引き起こすことがあります。例えば、スクリプト言語のパーサーがBOMをスクリプトの開始タグの前に見つけると、構文エラーになることがあります。
    • 可視性: BOMは通常のエディタでは表示されない不可視文字であるため、存在に気づきにくく、問題の原因特定を困難にすることがあります。
    • ファイル結合: 複数のBOM付きファイルを結合すると、ファイルの中間にBOMが出現し、上述のgrepの問題のような事態を招く可能性があります。

Go言語のソースコードとUnicode

Go言語は、ソースコードのエンコーディングとしてUTF-8を強く推奨し、事実上の標準としています。Goのコンパイラは、ソースファイルをUTF-8として解釈することを前提としています。

  • シンプルさの追求: Go言語の設計哲学の一つに「シンプルさ」があります。ソースコードのエンコーディングに関しても、複雑な設定や多様なエンコーディングのサポートを避け、UTF-8に一本化することで、コンパイラの設計を簡素化し、開発者の混乱を減らしています。
  • Unicodeのサポート: Goは文字列や文字(ルーン)をUnicodeコードポイントとして扱います。これにより、世界中の多様な言語の文字を扱うことが可能です。
  • ソースコードの正規化: Goのコンパイラは、ソースコードを内部的にUnicodeコードポイントのシーケンスとして処理します。この過程で、特定の不可視文字や、正規化形式の違い(NFC vs NFD)など、Unicodeの複雑な側面をどのように扱うかが仕様で定義されています。BOMの扱いは、このソースコードの正規化と解釈の一部として位置づけられます。

このコミットは、Go言語がBOMの扱いに関して、より厳格で予測可能な挙動を定義することで、ツールとの互換性を高め、開発者が遭遇する可能性のある潜在的な問題を未然に防ぐことを目的としています。

技術的詳細

このコミットにおける技術的な変更は、Go言語の仕様書 doc/go_spec.html 内の「実装上の制約 (Implementation restriction)」に関する記述の修正に集約されます。

変更前と変更後の記述を比較することで、その技術的な意味合いを深く理解できます。

変更前(旧仕様):

Implementation restriction: For compatibility with other tools, a
compiler may ignore any UTF-8-encoded Unicode byte order mark
(U+FEFF) in the source text outside of <a href="#String_literals">string</a>
and <a href="#Rune_literals">rune</a> literals.

この記述は、「コンパイラは、文字列リテラルとルーンリテラル以外のソーステキスト中の任意のUTF-8エンコードされたBOM(U+FEFF)を無視してもよい」と述べていました。

  • "any" (任意の): これは、BOMがファイルの先頭だけでなく、ファイルの途中、例えばコメント内や識別子の間など、どこにでも出現した場合でもコンパイラがそれを無視する可能性があることを意味していました。
  • "outside of string and rune literals": 文字列リテラルやルーンリテラル内部のBOMは、通常の文字として扱われるべきであり、無視されないという制約がありました。これは、BOMが意図的に文字列の一部として含まれる可能性があるためです。

変更後(新仕様):

Implementation restriction: For compatibility with other tools, a
compiler may ignore a UTF-8-encoded byte order mark
(U+FEFF) if it is the first Unicode code point in the source text.

この記述は、「コンパイラは、UTF-8エンコードされたBOM(U+FEFF)がソーステキストの最初のUnicodeコードポイントである場合にのみ、それを無視してもよい」と修正されました。

  • "if it is the first Unicode code point": この変更が最も重要です。BOMを無視する条件を、ファイルの先頭に限定しています。これにより、ファイルの中間に存在するBOMは、コンパイラによって無視されず、通常の文字として扱われるか、あるいは構文エラーを引き起こす可能性があります(Goの字句解析器がBOMを有効なトークンとして認識しない場合)。
  • "may ignore" (無視してもよい): 「無視しなければならない」ではなく「無視してもよい」という表現は維持されています。これは、コンパイラの実装がBOMを無視するかどうかは任意であることを示唆していますが、一般的なGoコンパイラは互換性のためにこれを無視するでしょう。

技術的な影響:

  1. 予測可能性の向上: 以前の「任意の場所」という曖昧な定義から、「ファイルの先頭のみ」という明確な定義に変わったことで、Goソースコードの字句解析の挙動がより予測可能になりました。開発者は、BOMがファイルの先頭以外に存在しないことを前提にコードを記述し、ツールを使用できるようになります。
  2. ツールとの互換性強化: コミットメッセージで述べられているように、grepのような外部ツールはBOMを通常の文字として扱います。もしBOMがファイルの中間に存在し、コンパイラがそれを無視する一方でgrepが無視しない場合、Goコンパイラでコンパイルできるコードがgrepでは正しく検索できないという不整合が生じます。この変更により、GoコンパイラもBOMをファイルの先頭以外では無視しない(またはエラーとする)ため、ツール間の挙動の乖離が解消され、互換性が向上します。
  3. 潜在的なバグの防止: ファイルの中間に意図せずBOMが挿入された場合、以前の仕様ではコンパイラがそれを無視するため、開発者はその存在に気づきにくい可能性がありました。しかし、この変更により、そのようなBOMはコンパイラによって認識され、場合によってはエラーとして報告されるため、潜在的なバグや意図しない挙動を防ぐことができます。
  4. Unicode標準への準拠: UTF-8におけるBOMは、通常ファイルの先頭にのみ使用されるというUnicodeの慣例に、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\",
-\t\"Subtitle\": \"Version of September 6, 2012\",
+\t\"Subtitle\": \"Version of September 7, 2012\",
  	\"Path\": \"/ref/spec\"\
 }-->
 
@@ -101,9 +101,8 @@ compiler may disallow the NUL character (U+0000) in the source text.\
 </p>\
 <p>\
 Implementation restriction: For compatibility with other tools, a\
-compiler may ignore any UTF-8-encoded Unicode byte order mark\
-(U+FEFF) in the source text outside of <a href=\"#String_literals\">string</a>\
-and <a href=\"#Rune_literals\">rune</a> literals.\
+compiler may ignore a UTF-8-encoded byte order mark\
+(U+FEFF) if it is the first Unicode code point in the source text.\
 </p>\
 \
 <h3 id=\"Characters\">Characters</h3>

具体的には、以下の2点が変更されています。

  1. 仕様書の更新日付: - \"Subtitle\": \"Version of September 6, 2012\", + \"Subtitle\": \"Version of September 7, 2012\", 仕様書のバージョン日付が2012年9月6日から2012年9月7日に更新されています。これは、このコミットによる変更が反映されたことを示します。

  2. BOMの扱いに関する記述の修正: -compiler may ignore any UTF-8-encoded Unicode byte order mark -(U+FEFF) in the source text outside of <a href=\"#String_literals\">string</a> -and <a href=\"#Rune_literals\">rune</a> literals. +compiler may ignore a UTF-8-encoded byte order mark +(U+FEFF) if it is the first Unicode code point in the source text. これが本質的な変更です。

    • 削除された行は、コンパイラが文字列リテラルとルーンリテラル以外のソーステキスト中の「任意の」BOMを無視できるとしていました。
    • 追加された行は、コンパイラがBOMを無視できるのは「それがソーステキストの最初のUnicodeコードポイントである場合」に限定されることを明確にしています。

コアとなるコードの解説

このコミットの「コアとなるコード」は、Go言語の公式仕様書である doc/go_spec.html の一部です。このファイルは、Go言語の構文、セマンティクス、および実装上の制約を定義する権威あるドキュメントです。したがって、このファイルへの変更は、Go言語自体の挙動に直接的な影響を与える、非常に重要な変更と見なされます。

変更された箇所のHTMLスニペットは、Goコンパイラがソースコードをどのように解釈するかに関する「実装上の制約」を記述しています。

変更前:

Implementation restriction: For compatibility with other tools, a
compiler may ignore any UTF-8-encoded Unicode byte order mark
(U+FEFF) in the source text outside of <a href="#String_literals">string</a>
and <a href="#Rune_literals">rune</a> literals.

この記述は、Goコンパイラが、ソースコード中の文字列リテラルやルーンリテラル以外の場所にあるBOM(U+FEFF)を「任意に(any)」無視してもよい、という柔軟な解釈を許容していました。これは、BOMがファイルの先頭だけでなく、中間に出現した場合でもコンパイラがそれを透過的に処理する可能性を示唆していました。この柔軟性は、一部のツールとの互換性を意図したものですが、同時に、BOMが意図しない場所に出現した場合の挙動の予測を困難にする側面も持っていました。

変更後:

Implementation restriction: For compatibility with other tools, a
compiler may ignore a UTF-8-encoded byte order mark
(U+FEFF) if it is the first Unicode code point in the source text.

この修正により、BOMを無視する条件が「それがソーステキストの最初のUnicodeコードポイントである場合」に限定されました。

  • if it is the first Unicode code point in the source text.: この条件が追加されたことで、Goコンパイラは、ファイルの先頭に存在するBOMのみを無視するべきであり、それ以外の場所(ファイルの中間など)に存在するBOMは無視しない、という明確な指針が示されました。
  • compiler may ignore: 「無視してもよい」という表現は維持されており、コンパイラの実装がBOMを無視するかどうかは依然として任意ですが、この変更により、無視する範囲が厳密に定義されました。

この仕様変更の意図は、Goソースコードの字句解析の挙動をより予測可能にし、grepのような外部ツールとの互換性を向上させることにあります。ファイルの中間にBOMが存在する場合、それは通常、意図しない挿入やエディタの設定ミスによるものであり、Goコンパイラがそれを無視しないことで、開発者はそのような問題を早期に発見できるようになります。結果として、Go言語のエコシステム全体における一貫性と堅牢性が向上します。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメントおよび仕様書
  • Unicode Consortiumの公式ウェブサイト
  • Wikipediaの関連ページ (UTF-8, Byte Order Mark)
  • Go言語のコミット履歴と関連するコードレビュー(https://golang.org/cl/6490091
  • 一般的なプログラミングにおけるBOMの扱いに関する技術記事