[インデックス 1498] ファイルの概要
このコミットは、Go言語の標準ライブラリの一部であるjson
パッケージ内のコード変更に関するものです。具体的には、以下の4つのファイルが変更されています。
src/lib/json/generic.go
src/lib/json/parse.go
src/lib/json/struct.go
src/lib/json/struct_test.go
これらのファイルは、JSONデータの解析、構築、およびGoの構造体とのマッピングを扱うための基本的な機能を提供しています。
コミット
- コミットハッシュ:
393df079148614c6c2be57ded233eeacc8a1c78a
- Author: Russ Cox rsc@golang.org
- Date: Fri Jan 16 10:14:38 2009 -0800
- 変更ファイル数: 4 files changed, 137 insertions(+), 137 deletions(-)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/393df079148614c6c2be57ded233eeacc8a1c78a
元コミット内容
casify json
R=r
DELTA=163 (0 added, 0 deleted, 163 changed)
OCL=22910
CL=22939
変更の背景
このコミットの目的は、Go言語のjson
パッケージ内の識別子(型、変数、関数など)の可視性を調整することです。コミットメッセージの「casify json」は、Go言語における識別子の命名規則(大文字/小文字)に基づいて、それらの公開/非公開(エクスポート/アンエクスポート)状態を変更することを意味しています。
Go言語では、識別子の最初の文字が大文字である場合、その識別子はパッケージ外に公開(エクスポート)され、他のパッケージからアクセス可能になります。一方、最初の文字が小文字である場合、その識別子はパッケージ内部でのみ使用可能(アンエクスポート)となり、外部からはアクセスできません。
この変更の背景には、Goの設計思想である「カプセル化」と「APIの明確化」があります。パッケージの内部実装の詳細を隠蔽し、外部に公開する必要のあるAPIのみを明確にすることで、パッケージの保守性、再利用性、および安定性を向上させることができます。json
パッケージはGoの標準ライブラリの一部であるため、そのAPI設計は特に重要であり、不必要な内部実装の公開は避けるべきです。
したがって、このコミットでは、json
パッケージの内部で使用されるが、外部に公開する必要のない型や関数を、命名規則に従ってアンエクスポート(小文字化)しています。これにより、パッケージの外部インターフェースがよりクリーンになり、将来的な内部実装の変更が外部に影響を与えるリスクが低減されます。
前提知識の解説
Go言語における識別子の可視性(エクスポート/アンエクスポート)
Go言語には、他の多くのプログラミング言語に見られるようなpublic
、private
、protected
といった明示的なアクセス修飾子が存在しません。代わりに、識別子(変数、関数、型、構造体のフィールドなど)の可視性は、その名前の最初の文字が大文字か小文字かによって決定されます。
-
エクスポートされた識別子 (Exported Identifiers):
- 名前の最初の文字が大文字で始まる識別子は、エクスポートされます。
- エクスポートされた識別子は、それを宣言しているパッケージをインポートする他のすべてのパッケージからアクセス可能です。
- これは、パッケージの公開APIの一部となります。
- 例:
MyFunction
,MyType
,MyVariable
-
アンエクスポートされた識別子 (Unexported Identifiers):
- 名前の最初の文字が小文字で始まる識別子は、アンエクスポートされます。
- アンエクスポートされた識別子は、それを宣言しているパッケージ内でのみ可視であり、外部のパッケージからは直接アクセスできません。
- これは、パッケージの内部実装の詳細を隠蔽するために使用されます。
- 例:
myFunction
,myType
,myVariable
このシンプルなルールにより、Goはコードのモジュール性とカプセル化を強制します。開発者は、パッケージの外部インターフェースとして何を提供し、何を内部実装の詳細として隠蔽するかを、命名規則を通じて明確にすることができます。
JSON (JavaScript Object Notation)
JSONは、人間が読み書きしやすく、機械が解析しやすい軽量なデータ交換フォーマットです。JavaScriptのオブジェクトリテラル表記法に基づいていますが、言語に依存しないデータフォーマットとして広く利用されています。
JSONは主に以下の2つの構造で構成されます。
- オブジェクト (Object): 波括弧
{}
で囲まれ、キーと値のペアの順序なしのコレクションです。キーは文字列で、値はJSONのデータ型(文字列、数値、真偽値、null、オブジェクト、配列)のいずれかです。 例:{"name": "Alice", "age": 30}
- 配列 (Array): 角括弧
[]
で囲まれ、値の順序付きリストです。値はJSONのデータ型のいずれかです。 例:["apple", "banana", "cherry"]
JSONは、WebアプリケーションにおけるAPI通信、設定ファイルの記述、データストレージなど、多岐にわたる用途で利用されています。Go言語のencoding/json
パッケージ(このコミットが関連するjson
パッケージの前身または初期バージョン)は、Goのデータ構造とJSONデータの間で変換を行う機能を提供します。
技術的詳細
このコミットの主要な技術的変更は、src/lib/json/generic.go
、src/lib/json/parse.go
、src/lib/json/struct.go
、src/lib/json/struct_test.go
の各ファイルにおいて、Goの識別子の命名規則に従い、内部的に使用される型や関数をアンエクスポート(小文字化)したことです。
具体的には、以下の変更が行われています。
-
型名の変更:
Null
->_Null
String
->_String
Number
->_Number
Array
->_Array
Bool
->_Bool
Map
->_Map
JsonBuilder
->_JsonBuilder
Lexer
->_Lexer
Value
->_Value
StructBuilder
->_StructBuilder
MyStruct
->_MyStruct
(テストファイル内)
これらの型は、JSONデータをGoの内部表現として扱うためのものであり、通常、パッケージの外部から直接インスタンス化されたり、参照されたりする必要はありません。アンエクスポートすることで、パッケージの内部実装の詳細として扱われ、外部からの不適切な利用を防ぎます。
-
変数名の変更:
null
->Null
(ただし、これは_Null
型に対応するエクスポートされたNull
シングルトンインスタンスとして残されています。これは、JSONのnull
値を表現するための公開された定数として機能するため、例外的に大文字のままです。ただし、その基底型は_Null
に変わっています。)nobuilder
->nobuilder
(これは既に小文字なので変更なし)Encoded
->_Encoded
(テストファイル内)
-
関数名の変更:
UnHex
->_UnHex
Punct
->punct
White
->white
SkipWhite
->skipwhite
SkipToken
->skiptoken
SkipString
->skipstring
ParseValue
->parse
SetFloat
->setfloat
SetInt
->setint
Check
->_Check
(テストファイル内)
これらの関数は、JSONの解析や構造体へのマッピングといった内部処理のロジックを構成するものであり、パッケージの外部から直接呼び出されることを意図していません。アンエクスポートすることで、パッケージの内部ヘルパー関数としての役割が明確になります。
これらの変更は、Goの命名規則に厳密に従い、パッケージの公開APIと内部実装を明確に分離することを目的としています。これにより、json
パッケージの利用者は、公開されたAPIのみに依存するようになり、内部実装の変更が外部のコードに影響を与えるリスクが大幅に減少します。また、コードの可読性と保守性も向上します。
コアとなるコードの変更箇所
以下に、src/lib/json/generic.go
における型定義の変更例を示します。
--- a/src/lib/json/generic.go
+++ b/src/lib/json/generic.go
@@ -44,40 +44,40 @@ export func JsonToString(j Json) string {
return j.String()
}
-type Null struct { }
-export var null Json = &Null{}
-func (*Null) Kind() int { return NullKind }
-func (*Null) String() string { return "null" }
-func (*Null) Number() float64 { return 0 }
-func (*Null) Bool() bool { return false }
-func (*Null) Get(s string) Json { return null }
-func (*Null) Elem(int) Json { return null }
-func (*Null) Len() int { return 0 }
-
-type String struct { s string; Null }
-func (j *String) Kind() int { return StringKind }
-func (j *String) String() string { return j.s }
-
-type Number struct { f float64; Null }
-func (j *Number) Kind() int { return NumberKind }
-func (j *Number) Number() float64 { return j.f }
-func (j *Number) String() string {
+type _Null struct { }
+export var Null Json = &_Null{}
+func (*_Null) Kind() int { return NullKind }
+func (*_Null) String() string { return "null" }
+func (*_Null) Number() float64 { return 0 }
+func (*_Null) Bool() bool { return false }
+func (*_Null) Get(s string) Json { return Null }
+func (*_Null) Elem(int) Json { return Null }
+func (*_Null) Len() int { return 0 }
+
+type _String struct { s string; _Null }
+func (j *_String) Kind() int { return StringKind }
+func (j *_String) String() string { return j.s }
+
+type _Number struct { f float64; _Null }
+func (j *_Number) Kind() int { return NumberKind }
+func (j *_Number) Number() float64 { return j.f }
+func (j *_Number) String() string {
if math.Floor(j.f) == j.f {
return fmt.Sprintf("%.0f", j.f);
}
return fmt.Sprintf("%g", j.f);
}
-type Array struct { a *array.Array; Null }
-func (j *Array) Kind() int { return ArrayKind }
-func (j *Array) Len() int { return j.a.Len() }
-func (j *Array) Elem(i int) Json {
+type _Array struct { a *array.Array; _Null }
+func (j *_Array) Kind() int { return ArrayKind }
+func (j *_Array) Len() int { return j.a.Len() }
+func (j *_Array) Elem(i int) Json {
if i < 0 || i >= j.a.Len() {
- return null
+ return Null
}
return j.a.At(i)
}
-func (j *Array) String() string {
+func (j *_Array) String() string {
s := "[";
for i := 0; i < j.a.Len(); i++ {
if i > 0 {
また、src/lib/json/parse.go
における関数名の変更例です。
--- a/src/lib/json/parse.go
+++ b/src/lib/json/parse.go
@@ -23,7 +23,7 @@ import (
// No literal control characters, supposedly.
// Have also seen \' and embedded newlines.
-func UnHex(p string, r, l int) (v int, ok bool) {
+func _UnHex(p string, r, l int) (v int, ok bool) {
v = 0;
for i := r; i < l; i++ {
if i >= len(p) {
@@ -86,7 +86,7 @@ export func Unquote(s string) (t string, ok bool) {
w++;
case 'u':
r++;
- rune, ok := UnHex(s, r, 4);
+ rune, ok := _UnHex(s, r, 4);
if !ok {
return
}
@@ -166,38 +166,38 @@ export func Quote(s string) string {
}
-// Lexer
+// _Lexer
-type Lexer struct {
+type _Lexer struct {
s string;
i int;
kind int;
token string;
}
-func Punct(c byte) bool {
+func punct(c byte) bool {
return c=='"' || c=='[' || c==']' || c==':' || c=='{' || c=='}' || c==','
}
-func White(c byte) bool {
+func white(c byte) bool {
return c==' ' || c=='\t' || c=='\n' || c=='\v'
}
-func SkipWhite(p string, i int) int {
- for i < len(p) && White(p[i]) {
+func skipwhite(p string, i int) int {
+ for i < len(p) && white(p[i]) {
i++
}
return i
}
-func SkipToken(p string, i int) int {
- for i < len(p) && !Punct(p[i]) && !White(p[i]) {
+func skiptoken(p string, i int) int {
+ for i < len(p) && !punct(p[i]) && !white(p[i]) {
i++
}
return i
}
-func SkipString(p string, i int) int {
+func skipstring(p string, i int) int {
for i++; i < len(p) && p[i] != '"'; i++ {
if p[i] == '\\' {
i++
@@ -209,9 +209,9 @@ func SkipString(p string, i int) int {
return i+1
}
-func (t *Lexer) Next() {
+func (t *_Lexer) Next() {
i, s := t.i, t.s;
- i = SkipWhite(s, i);
+ i = skipwhite(s, i);
if i >= len(s) {
t.kind = 0;
t.token = "";
コアとなるコードの解説
上記のコード変更は、Go言語の「エクスポート/アンエクスポート」ルールを適用した典型的な例です。
-
型名の変更 (
Null
->_Null
など):- 元のコードでは、
Null
,String
,Number
,Array
,Bool
,Map
といった型が定義されていました。これらはJSONの各データ型に対応するGoの内部表現です。 - これらの型名の先頭にアンダースコア
_
を付加することで、Goの命名規則上、これらの型はアンエクスポート(非公開)となります。 - これにより、
json
パッケージの外部からはこれらの型を直接参照したり、インスタンスを作成したりすることができなくなります。パッケージの利用者は、json
パッケージが提供する公開されたインターフェース(例:Json
インターフェースや、JSONの解析・生成関数)を通じてのみJSONデータを操作するようになります。これは、パッケージの内部実装の詳細を隠蔽し、APIの安定性を高めるための重要なステップです。 export var null Json = &Null{}
がexport var Null Json = &_Null{}
に変更されている点に注目してください。null
変数はNull
に大文字化されていますが、これはJSONのnull
値を表現する公開されたシングルトンインスタンスとして機能するためです。しかし、その基底となる型は内部的な_Null
型に変更されています。
- 元のコードでは、
-
関数名の変更 (
UnHex
->_UnHex
,Punct
->punct
など):UnHex
,Punct
,White
,SkipWhite
,SkipToken
,SkipString
,ParseValue
といった関数は、JSON文字列の字句解析や構文解析といった内部処理で使用されるヘルパー関数です。- これらの関数名の先頭を小文字に変更するか、アンダースコア
_
を付加することで、これらもアンエクスポート(非公開)となります。 - これにより、これらの関数は
json
パッケージの内部でのみ呼び出し可能となり、外部のパッケージからは直接アクセスできなくなります。これは、パッケージの内部ロジックをカプセル化し、外部からの不適切な利用や依存を防ぐための標準的なプラクティスです。
これらの変更は、Goのパッケージ設計におけるベストプラクティスに従ったものであり、json
パッケージがより堅牢で、保守しやすく、APIが明確なものとなるように貢献しています。
関連リンク
特になし。
参考にした情報源リンク
- Go language export unexport rules - Google Search
- Go: Exported vs. Unexported Identifiers - DigitalOcean
- Go: Exported vs. Unexported Identifiers - Medium
- Go: Exported vs. Unexported Identifiers - Labex.io
- The Go Programming Language Specification - Exported identifiers
- Go: Exported vs. Unexported Identifiers - YouTube (Note: This is a placeholder link and may not be relevant to the topic.)
- Go: Exported vs. Unexported Identifiers - Ardan Labs
- Go: Exported vs. Unexported Identifiers - Boot.dev
- Go: Exported vs. Unexported Identifiers - Stack Overflow
- Go: Exported vs. Unexported Identifiers - Rafaelfranco.es