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

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

このコミットは、Go言語の標準ライブラリである mime パッケージ内のいくつかの内部関数を非公開(unexported)にする変更です。具体的には、src/pkg/mime/grammar.go および src/pkg/mime/mediatype.go ファイルにおいて、外部から参照されるべきではない関数名の先頭を大文字から小文字に変更しています。これにより、パッケージの内部実装の詳細が外部に漏れることを防ぎ、APIの安定性と保守性を向上させています。

コミット

  • コミットハッシュ: 9df6fdcc1caa668429ffa7155e61236d3acf0905
  • Author: Brad Fitzpatrick bradfitz@golang.org
  • Date: Tue Feb 14 12:48:28 2012 +1100
  • コミットメッセージ:
    mime: unexport some internal details
    
    Fixes #2941
    
    R=golang-dev, rsc, adg
    CC=golang-dev
    https://golang.org/cl/5663046
    

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

https://github.com/golang/go/commit/9df6fdcc1caa668429ffa7155e61236d3acf0905

元コミット内容

このコミットの目的は、mime パッケージの内部詳細を非公開にすることです。コミットメッセージには「Fixes #2941」とあり、これはGoのIssueトラッカーにおける2941番の課題を解決したことを示しています。通常、このような変更は、APIの設計原則に従い、外部ユーザーが直接利用すべきではない内部ヘルパー関数やデータ構造を隠蔽するために行われます。これにより、将来的な内部実装の変更が外部APIに影響を与えるリスクを低減し、パッケージの安定性を高めます。

変更の背景

Go言語では、関数名や変数名の先頭が大文字で始まる場合、それはそのパッケージの外部からアクセス可能(exported)であることを意味します。一方、小文字で始まる場合は、そのパッケージ内でのみアクセス可能(unexported)であり、内部実装の詳細として扱われます。

このコミットが行われた背景には、mime パッケージのAPI設計の洗練と、内部実装の詳細を隠蔽するというGoの設計思想があります。IsTokenChar, IsToken, IsQText といった関数は、MIMEタイプやメールヘッダーの解析において内部的に使用されるヘルパー関数であり、外部の利用者が直接これらの関数に依存することは意図されていませんでした。しかし、これらが大文字で始まっていたため、誤って外部から利用される可能性がありました。

「Fixes #2941」という記述から、おそらくこのIssueでは、これらの関数が内部的なものであるにもかかわらず公開されていること、またはそれらが原因で何らかの問題が発生していることが指摘されたと考えられます。この変更により、mime パッケージの公開APIがより明確になり、内部実装の変更が外部に影響を与える可能性が減少します。

前提知識の解説

Go言語における公開/非公開(Exported/Unexported)の概念

Go言語では、識別子(変数、関数、型など)の可視性(visibility)は、その識別子の最初の文字が大文字か小文字かによって決まります。

  • 大文字で始まる識別子: パッケージの外部からアクセス可能(exported)。これは、その識別子がパッケージの公開APIの一部であることを意味します。
  • 小文字で始まる識別子: パッケージの内部からのみアクセス可能(unexported)。これは、その識別子がパッケージの内部実装の詳細であり、外部から直接利用されるべきではないことを意味します。

このルールは、Goのシンプルさと明示性を重視する設計哲学の一部であり、APIの意図を明確にし、不必要な依存関係を防ぐのに役立ちます。

RFC 1521, RFC 2045, RFC 822

このコミットで変更された関数は、MIMEタイプやメールヘッダーの構文解析に関連しています。これらの構文は、以下のRFC(Request for Comments)で定義されています。

  • RFC 1521 (MIME Part One: Mechanisms for Specifying and Describing the Format of Internet Message Bodies): マルチパートメッセージや非ASCII文字のエンコーディングなど、MIME(Multipurpose Internet Mail Extensions)の基本的なメカニズムを定義しています。このRFCは後にRFC 2045に置き換えられました。
  • RFC 2045 (MIME Part One: Format of Internet Message Bodies): RFC 1521を更新し、インターネットメッセージのボディのフォーマットを定義しています。MIMEタイプ(例: text/plain, application/json)の構文や、token の定義などが含まれます。
    • token: RFC 2045で定義される token は、MIMEヘッダーやパラメータ値で使用される、特定の文字セットから構成される連続した文字列です。スペース、制御文字、および tspecials (例: ()<>@,;:\/"[]?=) 以外のUS-ASCII文字の任意の組み合わせで構成されます。
  • RFC 822 (Standard for the Format of ARPA Internet Text Messages): インターネットメッセージ(電子メール)の標準フォーマットを定義しています。ヘッダーフィールドの構文や、qtext の定義などが含まれます。
    • qtext: RFC 822で定義される qtext は、引用符で囲まれた文字列(quoted-string)内で使用される文字セットです。引用符 (")、バックスラッシュ (\)、CR(キャリッジリターン)以外の任意のASCII文字が許容されます。

これらのRFCは、インターネットにおけるメッセージの構造と内容を標準化するための基盤であり、mime パッケージはこれらの仕様に準拠してMIMEタイプや関連するデータを処理します。

技術的詳細

mime パッケージは、MIMEメディアタイプを解析および生成するための機能を提供します。これは、HTTPヘッダーの Content-Type や電子メールの添付ファイルなど、様々な場所で利用されます。

このコミットで非公開化された関数は以下の通りです。

  • IsTokenChar(r rune) bool: RFC 1521およびRFC 2045で定義される token の構成文字であるかどうかを判定します。
  • IsToken(s string) bool: RFC 1521およびRFC 2045で定義される token であるかどうかを判定します。
  • IsQText(r int) bool: RFC 822で定義される qtext の構成文字であるかどうかを判定します。

これらの関数は、MIMEタイプやパラメータの構文解析において、文字列が特定のRFCの規則に準拠しているかを検証するために内部的に利用されていました。例えば、FormatMediaType 関数は、メディアタイプやその属性が token の規則に従っているかを IsToken を使ってチェックしていました。

これらの関数を非公開にすることで、mime パッケージの外部インターフェースがよりクリーンになります。外部のコードは、mime.ParseMediaTypemime.FormatMediaType といった公開された高レベルのAPIを通じてMIMEタイプを操作するべきであり、内部的な構文解析のヘルパー関数に直接依存すべきではありません。これにより、Goチームは将来的にこれらの内部関数の実装を自由に変更できるようになり、パッケージの進化が外部の互換性を損なうことなく行えるようになります。

コアとなるコードの変更箇所

このコミットでは、以下の2つのファイルが変更されています。

  1. src/pkg/mime/grammar.go
  2. src/pkg/mime/mediatype.go

具体的な変更内容は、関数名の先頭文字を大文字から小文字に変更することです。

src/pkg/mime/grammar.go の変更

--- a/src/pkg/mime/grammar.go
+++ b/src/pkg/mime/grammar.go
@@ -14,25 +14,25 @@ func isTSpecial(r rune) bool {
 	return strings.IndexRune(`()<>@,;:\`"/[]?=`, r) != -1
 }

-// IsTokenChar returns true if rune is in 'token' as defined by RFC
+// isTokenChar returns true if rune is in 'token' as defined by RFC
 // 1521 and RFC 2045.
-func IsTokenChar(r rune) bool {
+func isTokenChar(r rune) bool {
 	// token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
 	//             or tspecials>
 	return r > 0x20 && r < 0x7f && !isTSpecial(r)
 }

-// IsToken returns true if s is a 'token' as as defined by RFC 1521
+// isToken returns true if s is a 'token' as as defined by RFC 1521
 // and RFC 2045.
-func IsToken(s string) bool {
+func isToken(s string) bool {
 	if s == "" {
 		return false
 	}
 	return strings.IndexFunc(s, isNotTokenChar) < 0
 }

-// IsQText returns true if rune is in 'qtext' as defined by RFC 822.
-func IsQText(r int) bool {
+// isQText returns true if rune is in 'qtext' as defined by RFC 822.
+func isQText(r int) bool {
 	// CHAR        =  <any ASCII character>        ; (  0-177,  0.-127.)
 	// qtext       =  <any CHAR excepting <">,     ; => may be folded
 	//                "\" & CR, and including
  • IsTokenChar -> isTokenChar
  • IsToken -> isToken
  • IsQText -> isQText

src/pkg/mime/mediatype.go の変更

--- a/src/pkg/mime/mediatype.go
+++ b/src/pkg/mime/mediatype.go
@@ -23,7 +23,7 @@ func FormatMediaType(t string, param map[string]string) string {
 	\treturn ""
 	}
 	major, sub := t[:slash], t[slash+1:]
-\tif !IsToken(major) || !IsToken(sub) {
+\tif !isToken(major) || !isToken(sub) {
 	\treturn ""
 	}
 	var b bytes.Buffer
@@ -34,12 +34,12 @@ func FormatMediaType(t string, param map[string]string) string {
 	\t\tb.WriteByte(';')
 	\t\tb.WriteByte(' ')
-\t\tif !IsToken(attribute) {
+\t\tif !isToken(attribute) {
 	\t\t\treturn ""
 	\t\t}
 	\t\tb.WriteString(strings.ToLower(attribute))
 	\t\tb.WriteByte('=')
-\t\tif IsToken(value) {
+\t\tif isToken(value) {
 	\t\t\tb.WriteString(value)
 	\t\t\tcontinue
 	\t\t}
@@ -205,7 +205,7 @@ func decode2231Enc(v string) string {
 }

 func isNotTokenChar(r rune) bool {
-\treturn !IsTokenChar(r)
+\treturn !isTokenChar(r)
 }

 // consumeToken consumes a token from the beginning of provided
  • FormatMediaType 関数内で IsToken の呼び出しを isToken に変更。
  • isNotTokenChar 関数内で IsTokenChar の呼び出しを isTokenChar に変更。

コアとなるコードの解説

この変更は、Go言語の可視性ルールに厳密に従うためのものです。

  • src/pkg/mime/grammar.go にあった IsTokenChar, IsToken, IsQText は、MIMEタイプやメールヘッダーの構文解析における低レベルなヘルパー関数です。これらは mime パッケージの内部で利用されることを意図しており、外部のコードが直接これらの関数に依存することは、パッケージのAPI設計上望ましくありません。関数名を小文字にすることで、これらの関数がパッケージの内部実装の一部であることを明確にし、外部からの直接的なアクセスを禁止します。

  • src/pkg/mime/mediatype.go では、FormatMediaType 関数がMIMEタイプとそのパラメータをフォーマットする際に、isToken 関数(旧 IsToken)を使用して、メディアタイプや属性、値がRFCの token 規則に準拠しているかを検証しています。また、isNotTokenChar 関数も isTokenChar 関数(旧 IsTokenChar)を利用しています。これらの変更は、grammar.go での関数名の変更に合わせて、内部的な呼び出しを修正したものです。

この変更により、mime パッケージの公開APIは ParseMediaTypeFormatMediaType のような高レベルな関数に限定され、パッケージの内部構造が外部に露出することがなくなります。これは、APIの安定性を高め、将来的な内部実装の変更が外部の利用者に影響を与えるリスクを低減する上で非常に重要です。

関連リンク

参考にした情報源リンク