[インデックス 1025] ファイルの概要
このコミットは、Go言語の標準ライブラリであるfmt
パッケージ内のprint.go
ファイルに対するバグ修正と機能追加を扱っています。具体的には、bool
型の値の出力が適切に処理されていなかった問題と、float64
型の値の取得における型アサーションの誤りを修正しています。
コミット
commit 59f029cbf241b5f29e183320c417ce5059464ccd
Author: Rob Pike <r@golang.org>
Date: Sat Nov 1 16:37:53 2008 -0700
a couple of bugs in print.
1) bool wasn't handled (added '%t' for 'truth').
2) float64 had a typo.
TBR=rsc
DELTA=11 (10 added, 0 deleted, 1 changed)
OCL=18314
CL=18318
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/59f029cbf241b5f29e183320c417ce5059464ccd
元コミット内容
a couple of bugs in print.
1) bool wasn't handled (added '%t' for 'truth').
2) float64 had a typo.
TBR=rsc
DELTA=11 (10 added, 0 deleted, 1 changed)
OCL=18314
CL=18318
変更の背景
このコミットは、Go言語がまだ開発の初期段階にあった2008年に行われたものです。Go言語のfmt
パッケージは、C言語のprintf
に似た書式指定文字列を用いたフォーマット済みI/Oを提供することを目的としています。しかし、初期の実装では、すべてのデータ型が適切に処理されているわけではありませんでした。
このコミットの主な背景は以下の2点です。
bool
型の出力の欠如: 当時のfmt
パッケージのprint
機能は、bool
型の値を適切に文字列として表現するメカニズムが不足していました。これにより、ブール値を直接出力しようとすると、予期せぬ結果になったり、エラーが発生したりする可能性がありました。この問題を解決するため、ブール値専用のフォーマット指定子%t
が導入されました。float64
型の処理におけるバグ: 浮動小数点数、特にfloat64
型の値を内部的に処理する際に、誤った型アサーション(reflect.Float32Value
と誤って扱っていた)が存在していました。これは、float64
の正確な値が取得されず、結果として誤った出力につながる可能性のある潜在的なバグでした。
これらの問題は、fmt
パッケージの正確性と完全性を確保するために修正される必要がありました。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念と、当時のreflect
パッケージの動作に関する知識が必要です。
1. Go言語のfmt
パッケージ
fmt
パッケージは、Go言語におけるフォーマット済みI/O(入出力)を扱うための標準パッケージです。C言語のprintf
やscanf
に似た関数を提供し、様々なデータ型を整形して文字列として出力したり、文字列からデータを読み取ったりする機能を持っています。
Printf
系の関数: 書式指定文字列(例:%d
、%s
、%f
など)を使用して、引数の値を指定された形式で文字列に変換し、出力します。Print
系の関数: 引数をデフォルトの形式で出力します。Println
系の関数: 引数をデフォルトの形式で出力し、最後に改行を追加します。
2. Go言語のreflect
パッケージ
reflect
パッケージは、Goプログラムが実行時に自身の構造(型情報)を検査し、操作するための機能を提供します。これにより、ジェネリックなコードを書いたり、異なる型の値を動的に扱ったりすることが可能になります。
reflect.Value
: 実行時のGoの値を表す型です。この型を通じて、値の取得、設定、メソッドの呼び出しなどが行えます。reflect.Kind
: 型の基本的なカテゴリ(例:Int
、String
、Struct
、Bool
、Float64
など)を表す列挙型です。reflect.Value
からKind()
メソッドを使って取得できます。- 型アサーション: インターフェース型に格納されている具体的な値の型を、実行時にチェックし、その具体的な型として扱うための構文です。例えば、
v.(reflect.Float32Value)
は、v
がreflect.Float32Value
型であることをアサートしています。
3. printf
スタイルのフォーマット指定子
printf
系の関数で使用される特殊な文字の組み合わせで、引数の値をどのように整形して出力するかを指示します。
%d
: 整数を10進数で出力。%s
: 文字列を出力。%f
: 浮動小数点数を標準形式で出力。%t
: (このコミットで追加された)ブール値をtrue
またはfalse
として出力。
技術的詳細
このコミットは、src/lib/fmt/print.go
ファイル内の2つの主要な領域に焦点を当てた修正と機能追加を行っています。
1. getFloat
関数におけるfloat64
の型アサーションの修正
getFloat
関数は、reflect.Value
から浮動小数点数(float32
またはfloat64
)の値を取得することを目的としています。元のコードでは、reflect.Float64Kind
の場合に誤ってv.(reflect.Float32Value).Get()
を使用していました。これは、float64
の値をfloat32
として扱おうとするものであり、データの精度が失われたり、不正な値が返されたりする可能性のあるバグでした。
このコミットでは、この行をreturn float64(v.(reflect.Float64Value).Get()), true;
に修正し、float64
型の値はreflect.Float64Value
として正しくアサートされるようにしました。これにより、float64
の値が正確に取得され、fmt
パッケージの浮動小数点数出力の信頼性が向上しました。
2. doprintf
関数における%t
フォーマット指定子の追加とbool
型の処理
doprintf
関数は、書式指定文字列に基づいて値をフォーマットする主要なロジックを含んでいます。このコミット以前は、bool
型を直接フォーマットするための特定の指定子が存在しませんでした。
%t
指定子の導入: 新たにcase 't':
が追加され、フォーマット指定子%t
がbool
型のために予約されました。reflect.BoolValue
の処理:field.(reflect.BoolValue).Get()
を使用してbool
型の実際の値を取得し、その値がtrue
であれば文字列"true"
を、false
であれば文字列"false"
を生成するようにロジックが追加されました。これにより、fmt.Printf("%t", true)
のような呼び出しが期待通りに動作するようになりました。
3. doprint
関数におけるreflect.BoolKind
の追加
doprint
関数は、Println
やPrint
のような、書式指定文字列なしで値をデフォルト形式で出力する関数によって内部的に使用されます。この関数は、値のKind()
に基づいて異なる型の処理を分岐します。
reflect.BoolKind
の追加:switch field.Kind()
の中にcase reflect.BoolKind:
が追加されました。これにより、bool
型の値が渡された際に、p.fmt.boolean(field.(reflect.BoolValue).Get()).str()
という新しいロジックが実行されるようになりました。このロジックは、bool
値を"true"
または"false"
の文字列に変換する役割を担っています。
これらの変更により、Go言語のfmt
パッケージは、bool
型の値をより堅牢かつ正確に処理できるようになり、開発者がブール値を期待通りに整形して出力できるようになりました。
コアとなるコードの変更箇所
--- a/src/lib/fmt/print.go
+++ b/src/lib/fmt/print.go
@@ -197,7 +197,7 @@ func getFloat(v reflect.Value) (val float64, ok bool) {
case reflect.Float32Kind:
return float64(v.(reflect.Float32Value).Get()), true;
case reflect.Float64Kind:
- return float64(v.(reflect.Float32Value).Get()), true;
+ return float64(v.(reflect.Float64Value).Get()), true;
case reflect.Float80Kind:
break; // TODO: what to do here?
}
@@ -273,6 +273,14 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
fieldnum++;
s := "";
switch c {
+ // bool
+ case 't':
+ if field.(reflect.BoolValue).Get() {
+ s = "true";
+ } else {
+ s = "false";
+ }
+
// int
case 'b':
if v, signed, ok := getInt(field); ok {
@@ -369,6 +377,8 @@ func (p *P) doprint(v reflect.StructValue, is_println bool) {
p.add(' ')
}
switch field.Kind() {
+ case reflect.BoolKind:
+ s = p.fmt.boolean(field.(reflect.BoolValue).Get()).str();
case reflect.IntKind, reflect.Int8Kind, reflect.Int16Kind, reflect.Int32Kind, reflect.Int64Kind:
v, signed, ok := getInt(field);
s = p.fmt.d64(v).str();
コアとなるコードの解説
1. src/lib/fmt/print.go
の getFloat
関数
@@ -197,7 +197,7 @@ func getFloat(v reflect.Value) (val float64, ok bool) {
case reflect.Float32Kind:
return float64(v.(reflect.Float32Value).Get()), true;
case reflect.Float64Kind:
- return float64(v.(reflect.Float32Value).Get()), true;
+ return float64(v.(reflect.Float64Value).Get()), true;
case reflect.Float80Kind:
break; // TODO: what to do here?
}
- 変更前:
case reflect.Float64Kind:
の行で、float64
型の値を処理する際に、誤ってv.(reflect.Float32Value).Get()
と記述されていました。これは、reflect.Value
がfloat64
型であるにもかかわらず、float32Value
としてアサートしようとしていたため、型アサーションの失敗や、値の不正な変換を引き起こす可能性がありました。 - 変更後:
return float64(v.(reflect.Float64Value).Get()), true;
に修正されました。これにより、float64
型の値はreflect.Float64Value
として正しくアサートされ、その値が正確に取得されるようになりました。これは、fmt
パッケージが浮動小数点数を正しく扱うための重要なバグ修正です。
2. src/lib/fmt/print.go
の doprintf
関数
@@ -273,6 +273,14 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
fieldnum++;
s := "";
switch c {
+ // bool
+ case 't':
+ if field.(reflect.BoolValue).Get() {
+ s = "true";
+ } else {
+ s = "false";
+ }
+
// int
case 'b':
if v, signed, ok := getInt(field); ok {
- 追加箇所:
switch c
(フォーマット指定子を処理する部分)の中に、新たにcase 't':
が追加されました。 - 機能: このブロックは、フォーマット指定子
%t
が検出された場合に実行されます。field.(reflect.BoolValue).Get()
を使って、現在のフィールドがreflect.BoolValue
であることをアサートし、そのブール値を取得します。取得した値がtrue
であれば文字列"true"
を、false
であれば文字列"false"
をs
変数に代入します。これにより、fmt.Printf("%t", someBoolVar)
のようにブール値を整形して出力できるようになりました。
3. src/lib/fmt/print.go
の doprint
関数
@@ -369,6 +377,8 @@ func (p *P) doprint(v reflect.StructValue, is_println bool) {
p.add(' ')
}
switch field.Kind() {
+ case reflect.BoolKind:
+ s = p.fmt.boolean(field.(reflect.BoolValue).Get()).str();
case reflect.IntKind, reflect.Int8Kind, reflect.Int16Kind, reflect.Int32Kind, reflect.Int64Kind:
v, signed, ok := getInt(field);
s = p.fmt.d64(v).str();
- 追加箇所:
switch field.Kind()
(値のKind
に基づいて処理を分岐する部分)の中に、新たにcase reflect.BoolKind:
が追加されました。 - 機能: このブロックは、
field
のKind
がreflect.BoolKind
(つまり、値がブール型)である場合に実行されます。p.fmt.boolean(field.(reflect.BoolValue).Get()).str()
という呼び出しは、reflect.BoolValue
からブール値を取得し、それをfmt
パッケージ内部のヘルパー関数boolean
に渡して文字列("true"
または"false"
)に変換し、その結果をs
変数に代入します。これにより、fmt.Print(someBoolVar)
やfmt.Println(someBoolVar)
のように、書式指定なしでブール値を出力する際に、適切に文字列化されるようになりました。
これらの変更は、Go言語のfmt
パッケージが、ブール型とfloat64
型をより正確かつ柔軟に扱えるようにするための基盤を築きました。
関連リンク
- Go言語の公式ドキュメント(現在の
fmt
パッケージ): https://pkg.go.dev/fmt - Go言語の公式ドキュメント(現在の
reflect
パッケージ): https://pkg.go.dev/reflect
参考にした情報源リンク
- Go言語のGitHubリポジトリ: https://github.com/golang/go
- コミットハッシュ
59f029cbf241b5f29e183320c417ce5059464ccd
の詳細ページ: https://github.com/golang/go/commit/59f029cbf241b5f29e183320c417ce5059464ccd - (必要に応じて、初期のGo言語のドキュメントやブログ記事をWeb検索で参照)
- 今回は、提供されたコミット情報とGo言語の基本的な知識で十分な解説が可能と判断したため、追加のWeb検索は行いませんでした。