[インデックス 10615] ファイルの概要
このコミットは、Go言語の標準ライブラリである strconv パッケージのAPIを刷新し、より一貫性のある命名規則と機能統合を導入するものです。文字列と数値、真偽値、引用符付き文字列間の変換を行う関数群が広範囲にわたって変更されています。
コミット
commit efbeaedb64e426f6874468ea4095d509622514df
Author: Russ Cox <rsc@golang.org>
Date: Mon Dec 5 15:48:21 2011 -0500
strconv: new API
R=golang-dev, bradfitz, gri, r, agl
CC=golang-dev
https://golang.org/cl/5434095
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/efbeaedb64e426f6874468ea4095d509622514df
元コミット内容
strconv: new API
変更の背景
このコミットの主な背景は、Go言語の strconv パッケージにおけるAPIの一貫性と使いやすさの向上です。以前のAPIは、文字列から数値への変換(Ato*)、数値から文字列への変換(*toa)、特定のビットサイズへの変換(*N)など、機能ごとに異なる命名規則が混在していました。これにより、開発者がどの関数を使用すべきか迷う可能性がありました。
この変更は、以下の目的を達成するために行われました。
- 命名規則の統一:
Parse*(文字列から型へ)、Format*(型から文字列へ)、Append*(バイトスライスに文字列形式を追加) という明確な命名規則を導入し、APIの予測可能性を高める。 - 機能の統合: 浮動小数点数や整数のパースにおいて、ビットサイズを引数として渡すことで、複数の専用関数(例:
Atof32,Atof64,AtofN)を単一の関数(ParseFloat)に統合し、APIの数を削減する。 - 効率性の向上:
Append*関数群を導入することで、既存のバイトスライスに変換結果を直接追加できるようになり、不要なメモリ割り当てを減らし、パフォーマンスを向上させる。これは、特にループ内で文字列変換を頻繁に行う場合に有効です。 - Go言語のイディオムへの準拠: Go言語の設計思想である「シンプルさ」と「明瞭さ」に沿ったAPIを提供することを目指しています。
これらの変更により、strconv パッケージはより直感的で、効率的で、Go言語の他の部分との整合性が高まることが期待されます。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念と strconv パッケージの役割について理解しておく必要があります。
strconvパッケージ: Go言語の標準ライブラリの一部で、プリミティブ型(真偽値、整数、浮動小数点数)と文字列の間で変換を行うための機能を提供します。例えば、文字列 "123" を整数 123 に変換したり、浮動小数点数 3.14 を文字列 "3.14" に変換したりする際に使用されます。- 型変換(Parsing/Formatting):
- Parsing (パース): 文字列形式のデータを、対応するGoのデータ型(例:
int,float64,bool)に変換するプロセスです。例えば、"123"をint(123)に変換します。 - Formatting (フォーマット): Goのデータ型を、その文字列表現に変換するプロセスです。例えば、
int(123)を"123"に変換します。
- Parsing (パース): 文字列形式のデータを、対応するGoのデータ型(例:
- ビットサイズ(bitSize): 整数型や浮動小数点数型が占めるメモリ上のビット数を指します。Goでは、
int8,int16,int32,int64やfloat32,float64など、異なるビットサイズの型が提供されています。strconvパッケージの変換関数では、このビットサイズを指定することで、変換後の数値が特定の型に収まるように制御できます。 append関数: Go言語の組み込み関数で、スライスに要素を追加するために使用されます。このコミットで導入されるAppend*関数は、変換結果の文字列を既存のバイトスライスに効率的に追加するために利用されます。- エラーハンドリング: Go言語では、関数がエラーを返す場合、通常は戻り値の最後の要素として
error型の値を返します。strconvパッケージの変換関数も、変換に失敗した場合に*NumError型のエラーを返します。 - IEEE 754 浮動小数点数標準: 浮動小数点数の表現と演算に関する国際標準です。
strconvパッケージの浮動小数点数変換は、この標準に準拠しています。特に、丸め処理(unbiased rounding)は、最も近い偶数への丸め(round half to even)を意味し、統計的なバイアスを避けるために使用されます。
これらの知識があることで、コミットがなぜ特定の関数名を変更し、新しい引数を導入し、特定の関数を追加したのかを深く理解することができます。
技術的詳細
このコミットは、strconv パッケージのAPIを大幅に再設計しています。主要な変更点は以下の通りです。
-
命名規則の統一と機能の統合:
- 真偽値変換:
Atob(str string) (value bool, err error)はParseBool(str string) (value bool, err error)に変更されました。Btoa(b bool) stringはFormatBool(b bool) stringに変更されました。- 新たに
AppendBool(dst []byte, b bool) []byteが追加され、バイトスライスへの効率的な追加が可能になりました。
- 浮動小数点数変換:
Atof32(s string) (f float32, err error),Atof64(s string) (f float64, err error),AtofN(s string, n int) (f float64, err error)の3つの関数が、単一のParseFloat(s string, bitSize int) (f float64, err error)に統合されました。bitSize引数(32または64)によって、パースする浮動小数点数の精度を指定します。戻り値は常にfloat64ですが、bitSize=32の場合はfloat32に変換しても値が変わらないことが保証されます。Ftoa32(f float32, fmt byte, prec int) string,Ftoa64(f float64, fmt byte, prec int) string,FtoaN(f float64, fmt byte, prec int, n int) stringの3つの関数が、単一のFormatFloat(f float64, fmt byte, prec int, bitSize int) stringに統合されました。同様にbitSize引数で元の浮動小数点数の精度を指定します。- 新たに
AppendFloat(dst []byte, f float64, fmt byte, prec int, bitSize int) []byteが追加されました。
- 整数変換:
Btoui64(s string, b int) (n uint64, err error)はParseUint(s string, base int, bitSize int) (n uint64, err error)に変更されました。bitSize引数(0, 8, 16, 32, 64)で、結果が収まるべき符号なし整数型を指定します。bitSize=0はintまたはuintのデフォルトサイズを意味します。Btoi64(s string, base int) (i int64, err error)はParseInt(s string, base int, bitSize int) (i int64, err error)に変更されました。同様にbitSize引数で符号付き整数型を指定します。Atoui64,Atoui,Atoi64,Atoiといった特定の型や基数に特化した関数は削除または簡略化され、ParseUintやParseIntのラッパーとして再定義されました。例えば、Atoi(s string) (i int, err error)はParseInt(s, 10, 0)のショートハンドとなりました。Uitob64(u uint64, base uint) stringはFormatUint(i uint64, base int) stringに変更されました。Itob64(i int64, base uint) stringはFormatInt(i int64, base int) stringに変更されました。Itoa64,Uitoa64,Uitob,Itob,Uitoaといった関数は削除または簡略化され、FormatUintやFormatIntのラッパーとして再定義されました。- 新たに
AppendInt(dst []byte, i int64, base int) []byteとAppendUint(dst []byte, i uint64, base int) []byteが追加されました。
- 引用符付き文字列変換:
Quote,QuoteToASCII,QuoteRune,QuoteRuneToASCIIに対応するAppendQuote,AppendQuoteToASCII,AppendQuoteRune,AppendQuoteRuneToASCII関数が追加されました。これらは、変換結果を既存のバイトスライスに直接追加します。
- 真偽値変換:
-
IntSize定数の変更:- 以前は
computeIntsize()関数で実行時に計算されていたIntSize(intおよびuintのビット数)が、コンパイル時に決定される定数const intSize = 32 << uint(^uint(0)>>63)に変更されました。これは、Goのコンパイラがターゲットアーキテクチャのポインタサイズに基づいてintのサイズを決定できるようになったためです。^uint(0)はすべてのビットが1のuint値を生成し、>>63はその値が64ビットシステムでは0、32ビットシステムでは1になることを利用して、シフト量を調整しています。
- 以前は
これらの変更により、strconv パッケージのAPIはより統一され、柔軟性が増し、パフォーマンスが向上しました。特に、bitSize 引数の導入により、異なる数値型への変換を単一の関数で扱えるようになった点は大きな改善です。また、Append* 関数群は、文字列操作におけるメモリ効率を高める上で重要な役割を果たします。
コアとなるコードの変更箇所
このコミットでは、src/pkg/strconv ディレクトリ内の複数のファイルが変更されています。主要な変更箇所は以下の通りです。
src/pkg/strconv/atob.go:Atob関数がParseBoolにリネーム。Btoa関数がFormatBoolにリネーム。AppendBool関数が新規追加。
src/pkg/strconv/atof.go:Atof32,Atof64,AtofN関数が削除され、ParseFloat関数が新規追加。ParseFloatはbitSize引数を受け取る。- 内部関数
atof32とatof64が追加され、ParseFloatから呼び出される。
src/pkg/strconv/atoi.go:Btoui64関数がParseUintにリネームされ、bitSize引数が追加。Btoi64関数がParseIntにリネームされ、bitSize引数が追加。Atoui64,Atoui,Atoi64,Atoi関数が削除または簡略化され、ParseUintやParseIntのラッパーとして再定義。IntSizeが実行時計算からコンパイル時定数に変更。
src/pkg/strconv/ftoa.go:Ftoa32,Ftoa64,FtoaN関数が削除され、FormatFloat関数が新規追加。FormatFloatはbitSize引数を受け取る。AppendFloat関数が新規追加。
src/pkg/strconv/itoa.go:Uitob64関数がFormatUintにリネームされ、base引数の型がuintからintに変更。Itob64関数がFormatIntにリネームされ、base引数の型がuintからintに変更。Itoa64,Uitoa64,Uitob,Itob,Uitoa関数が削除または簡略化され、FormatUintやFormatIntのラッパーとして再定義。AppendIntおよびAppendUint関数が新規追加。
src/pkg/strconv/quote.go:AppendQuote,AppendQuoteToASCII,AppendQuoteRune,AppendQuoteRuneToASCII関数が新規追加。
- 対応する
_test.goファイル群:- 上記API変更に伴い、各テストファイル内の関数呼び出しやテストケースが新しいAPIに合わせて更新されています。
これらの変更は、strconv パッケージ全体のAPI設計思想を根本的に変更するものであり、Go言語の進化における重要なステップの一つと言えます。
コアとなるコードの解説
ここでは、変更された主要な関数とその意図について、具体的なコードスニペットを交えながら解説します。
ParseBool (旧 Atob)
// 旧: func Atob(str string) (value bool, err error) { ... }
// 新: func ParseBool(str string) (value bool, err error) {
// switch str {
// case "1", "t", "T", "true", "TRUE", "True":
// return true, nil
// case "0", "f", "F", "false", "FALSE", "False":
// return false, nil
// }
// return false, &NumError{str, ErrSyntax}
// }
Atob から ParseBool へのリネームは、文字列を真偽値に「パースする」という関数の役割をより明確に示しています。Go言語の標準ライブラリでは、文字列から値を変換する関数には Parse プレフィックスを使用する慣習があります。
ParseFloat (旧 Atof32, Atof64, AtofN)
// 旧: func Atof32(s string) (f float32, err error) { ... }
// 旧: func Atof64(s string) (f float64, err error) { ... }
// 旧: func AtofN(s string, n int) (f float64, err error) { ... }
// 新: func ParseFloat(s string, bitSize int) (f float64, err error) {
// if bitSize == 32 {
// f1, err1 := atof32(s) // 内部関数
// return float64(f1), err1
// }
// f1, err1 := atof64(s) // 内部関数
// return f1, err1
// }
これは最も重要な変更点の一つです。異なるビットサイズの浮動小数点数パースを ParseFloat という単一の関数に統合しました。bitSize 引数によって、32ビット(float32)または64ビット(float64)の精度でパースするかを指定します。これにより、APIの複雑さが大幅に軽減され、開発者はより汎用的な関数を使用できるようになりました。戻り値が常に float64 であるのは、Goの型システムにおける浮動小数点数の扱いを簡素化するためです。
ParseUint (旧 Btoui64) と ParseInt (旧 Btoi64)
// 旧: func Btoui64(s string, b int) (n uint64, err error) { ... }
// 新: func ParseUint(s string, b int, bitSize int) (n uint64, err error) { ... }
// 旧: func Btoi64(s string, base int) (i int64, err error) { ... }
// 新: func ParseInt(s string, base int, bitSize int) (i int64, err error) { ... }
整数パース関数も ParseFloat と同様に bitSize 引数を導入し、特定のビットサイズ(8, 16, 32, 64)の整数をパースできるようになりました。これにより、Atoui, Atoi などの特定の型に特化した関数が不要になり、APIが整理されました。base 引数は、数値の基数(2進数から36進数まで)を指定します。
FormatFloat (旧 Ftoa32, Ftoa64, FtoaN)
// 旧: func Ftoa32(f float32, fmt byte, prec int) string { ... }
// 旧: func Ftoa64(f float64, fmt byte, prec int) string { ... }
// 旧: func FtoaN(f float64, fmt byte, prec int, n int) string { ... }
// 新: func FormatFloat(f float64, fmt byte, prec int, n int) string {
// if n == 32 {
// return genericFtoa(uint64(math.Float32bits(float32(f))), fmt, prec, &float32info)
// }
// return genericFtoa(math.Float64bits(f), fmt, prec, &float64info)
// }
浮動小数点数のフォーマットも FormatFloat に統合され、bitSize 引数で元の浮動小数点数の精度を指定できるようになりました。これにより、正確な丸め処理と桁数の決定が可能になります。
Append* 関数群
// 例: AppendBool
// func AppendBool(dst []byte, b bool) []byte {
// if b {
// return append(dst, "true"...)
// }
// return append(dst, "false"...)
// }
// 例: AppendInt
// func AppendInt(dst []byte, i int64, base int) []byte {
// return append(dst, FormatInt(i, base)...)
// }
// 例: AppendQuote
// func AppendQuote(dst []byte, s string) []byte {
// return append(dst, Quote(s)...)
// }
Append* 関数群は、既存のバイトスライス dst に変換結果の文字列を追加し、拡張されたバイトスライスを返す新しいパターンです。これは、特にパフォーマンスが重要なアプリケーションにおいて、文字列変換のたびに新しい文字列を生成するオーバーヘッドを削減するために導入されました。これにより、メモリ割り当てが減り、ガベージコレクションの負荷が軽減されます。
これらの変更は、strconv パッケージのAPIをより現代的で、Go言語のイディオムに沿ったものにし、開発者にとってより使いやすく、効率的なものにすることを目的としています。
関連リンク
- Go言語公式ドキュメント: https://golang.org/pkg/strconv/
- Go言語のコミット履歴: https://github.com/golang/go/commits/master
参考にした情報源リンク
- Go言語のコミット: https://github.com/golang/go/commit/efbeaedb64e426f6874468ea4095d509622514df
- Go Code Review Comments (API Design): https://go.dev/doc/effective_go#Getters (一般的なGoのAPI設計原則)
- IEEE 754 Standard for Floating-Point Arithmetic: https://standards.ieee.org/ieee/754/6210/ (浮動小数点数に関する背景知識)
- Go言語の
append関数に関するドキュメント: https://go.dev/blog/slices (スライスとappendの動作に関する詳細) - Go言語の
int型のサイズに関する議論 (当時の背景): https://go.dev/doc/faq#int_size (Goのint型のサイズがプラットフォーム依存であることに関する情報)