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

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

このコミットは、Go言語の標準ライブラリであるfmtパッケージのドキュメンテーションを大幅に改善することを目的としています。特に、fmtパッケージ内の主要な型、関数、およびインターフェースに対して、詳細なコメントを追加し、その振る舞いや意図を明確にしています。これにより、fmtパッケージの利用者が、各機能の役割や使い方をより深く理解できるようになります。また、一部の内部的な定数定義の改善や、%sフォーマット動詞におけるString()インターフェースの利用に関する挙動の明確化も含まれています。

コミット

commit 85647c94e60340285e1e15c854662fc978b4df4e
Author: Rob Pike <r@golang.org>
Date:   Fri Mar 6 03:35:38 2009 -0800

    document fmt.
    the description of the format verbs still needs to be done.
    
    R=rsc
    DELTA=288  (88 added, 12 deleted, 188 changed)
    OCL=25814
    CL=25833

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

https://github.com/golang/go/commit/85647c94e60340285e1e15c854662fc978b4df4e

元コミット内容

fmtパッケージのドキュメントを追加する。フォーマット動詞の記述はまだ完了していない。

変更の背景

Go言語の初期段階において、fmtパッケージはC言語のprintfに似た機能を提供していましたが、その内部実装や利用方法に関する公式なドキュメンテーションは不足していました。特に、Goの型システムとリフレクションの活用により、C言語のような厳密な型指定(例: %llud)が不要であるというGoのfmtの利点が十分に伝わっていませんでした。

このコミットの主な背景は、fmtパッケージのコードベースに直接、その機能、目的、および使用方法を説明する包括的なコメントを追加することでした。これにより、開発者がコードを読み解く際に、その場でドキュメントを参照できるようになり、学習コストの削減とコードの理解促進が図られます。また、fmtパッケージが提供するFmt構造体のような「通常の利用を意図しない」内部的なコンポーネントについても、その役割を明確にすることで、誤用を防ぐ狙いもありました。

さらに、fmtパッケージがカスタム型をどのように扱うか、特にString()メソッドやFormatterインターフェースを実装した型がどのようにフォーマットされるかについての説明も不足していました。このコミットは、これらの重要な概念をドキュメント化し、fmtパッケージの柔軟性と拡張性を明確にすることを目指しています。

前提知識の解説

Go言語のfmtパッケージ

fmtパッケージは、Go言語におけるフォーマットされたI/O(入出力)を扱うための標準ライブラリです。C言語のprintf関数群にインスパイアされており、文字列のフォーマット、標準出力への出力、ファイルへの出力など、様々な形式でのデータ表現を可能にします。

fmtパッケージの主な特徴は以下の通りです。

  • 型安全なフォーマット: Goのリフレクション機能を利用することで、C言語のように%d%sといったフォーマット動詞に加えて、引数の型を自動的に判断し、適切なフォーマットを適用します。これにより、%lludのような冗長な指定が不要になります。
  • PrintPrintfPrintln関数群:
    • Print系: 引数をデフォルトのフォーマットで出力します。文字列でない引数の間にはスペースが追加されます。
    • Printf系: フォーマット文字列と引数を受け取り、指定されたフォーマットで出力します。
    • Println系: Print系と同様にデフォルトのフォーマットで出力しますが、引数の間には常にスペースが追加され、末尾に改行が追加されます。
  • FprintFprintfFprintln関数群: Print系関数と同様の機能を提供しますが、出力先をio.Writerインターフェースを実装した任意のオブジェクト(例: ファイル、ネットワーク接続)に指定できます。
  • SprintSprintfSprintln関数群: Print系関数と同様の機能を提供しますが、結果を文字列として返します。
  • フォーマット動詞: %d(整数)、%s(文字列)、%v(任意の型のデフォルト表現)、%t(真偽値)、%x(16進数)など、様々なフォーマット動詞が用意されています。
  • 幅と精度: フォーマット動詞に加えて、出力の幅(最小文字数)や精度(浮動小数点数の小数点以下の桁数、文字列の最大文字数など)を指定できます。
  • フラグ: #(代替フォーマット)、+(符号の表示)、 (スペース埋め)などのフラグを使用して、出力形式をさらに制御できます。

String()メソッドとfmt.Stringerインターフェース

Go言語では、カスタム型が自身の文字列表現を定義するために、String()メソッドを実装することが慣習となっています。

type Stringer interface {
    String() string
}

fmtパッケージの関数(例: PrintPrintlnSprintf、または%s%vフォーマット動詞)は、引数がfmt.Stringerインターフェースを実装している場合、そのString()メソッドの戻り値を使用してオブジェクトをフォーマットします。これにより、開発者は独自の型がどのように文字列として表現されるかを柔軟に制御できます。

fmt.Formatterインターフェース

fmt.Formatterインターフェースは、より高度なカスタムフォーマットを可能にするためのインターフェースです。

type Formatter interface {
    Write(b []byte) (ret int, err *os.Error)
    Width() (wid int, ok bool)
    Precision() (prec int, ok bool)
    Flag(int) bool
}

このインターフェースを実装する型は、Format(f Formatter, c int)メソッドを通じて、fmtパッケージの内部的なフォーマッタの状態(幅、精度、フラグなど)にアクセスし、より詳細なフォーマットロジックを記述できます。c引数は、使用されたフォーマット動詞(例: 's''d')を表します。

utf8.RuneSelf

Go言語では、文字列はUTF-8エンコードされたバイト列として扱われます。rune型はUnicodeコードポイントを表します。utf8.RuneSelfは、UTF-8エンコードにおいて、ASCII文字(0-127)が1バイトで表現されることを示す定数です。この値は0x80(128)であり、これ未満のコードポイントは自己完結型(self-contained)のバイト列として扱われます。

技術的詳細

このコミットは、主にsrc/lib/fmt/format.gosrc/lib/fmt/print.goの2つのファイルにわたるドキュメンテーションの追加と、それに伴ういくつかの小さなコードの改善を含んでいます。

src/lib/fmt/format.goの変更点

  • Fmt構造体のコメントの改善:
    • 以前の簡潔なコメントが削除され、より詳細な説明が追加されました。
    • FmtPrintfなどの内部で使われる「生のフォーマッタ」であり、通常の利用を意図していないことが明記されました。
    • Fmtのモデル(内部バッファにオペランドを蓄積し、Str()Putnl()などで一度に取得する)と、メソッドチェーンの利用方法が例とともに説明されました。
  • Fmtメソッドへのコメント追加:
    • New()Str()Put()Putnl()Wp()P()W()といったFmtのパブリックメソッドすべてに、その機能と役割を説明するコメントが追加されました。
  • フォーマット関数(Fmt_boolean, Fmt_d64, Fmt_x64など)へのコメント追加と引数名の変更:
    • Fmt_booleanFmt_d64Fmt_x64Fmt_o64Fmt_b64Fmt_cFmt_sFmt_sxFmt_sXFmt_qFmt_e64Fmt_f64Fmt_g64Fmt_fb64、およびそれらの32ビット版やuint版のすべてのフォーマット関数に、そのフォーマットの目的を説明するコメントが追加されました。
    • これらの関数の引数名が、以前の一般的なaから、より意味のあるv(value)に変更されました。これはコードの可読性を向上させるための小さな改善です。

src/lib/fmt/print.goの変更点

  • パッケージレベルのコメントの追加:
    • ファイル冒頭に、fmtパッケージ全体の目的、C言語のprintfとの類似点、Goのリフレクションによる型推論の利点、%v動詞の汎用性、Print/Printlnバリアントの存在、String()メソッドとFormatterインターフェースの役割など、包括的な説明が追加されました。
  • FormatterFormatStringインターフェースへのコメント追加:
    • 各インターフェースの目的と、それが提供するメソッド(Write, Width, Precision, Flag for Formatter)について詳細なコメントが追加されました。
    • 特にStringインターフェースについては、%s%v、あるいは非フォーマット出力(Printなど)でどのように利用されるかが明確に説明されました。
  • runeSelf定数の変更:
    • const runeSelf = 0x80からconst runeSelf = utf8.RuneSelfに変更されました。これは、マジックナンバーを避け、utf8パッケージで定義された公式の定数を使用することで、コードの正確性と保守性を向上させるための変更です。
  • PrintfPrintPrintln系関数へのコメント追加:
    • FprintfPrintfSprintfFprintPrintSprintFprintlnPrintlnSprintlnといった、fmtパッケージの主要なI/O関数すべてに、その機能と出力の振る舞いを説明するコメントが追加されました。
  • doprintf関数内の%sフォーマット処理の改善:
    • %sフォーマット動詞の処理において、引数がStringインターフェース(String() stringメソッドを持つ型)を実装している場合に、そのString()メソッドの戻り値を使用するようにロジックが追加されました。これにより、カスタム型が%sで適切にフォーマットされるようになり、fmtパッケージの柔軟性が向上しました。

これらの変更は、Go言語のfmtパッケージが提供する強力なフォーマット機能を、より多くの開発者が理解し、効果的に利用できるようにするための重要なステップでした。特に、初期のGo言語ではドキュメンテーションがコード内に直接記述されることが多く、このコミットはその典型的な例と言えます。

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

このコミットのコアとなる変更は、主に以下の2つのファイルにおけるコメントの追加と、src/lib/fmt/print.goにおける%sフォーマット処理のロジック追加です。

src/lib/fmt/format.go

--- a/src/lib/fmt/format.go
+++ b/src/lib/fmt/format.go
@@ -8,14 +8,6 @@ import (
  	"strconv";
  )
  
-/*
-	Raw formatter. See print.go for a more palatable interface.
-
-	f := fmt.New();
-	print f.Fmt_d(1234).Fmt_s("\n").Str();  // create string, print it
-	f.Fmt_d(-1234).Fmt_s("\n").Put();  // print string
-	f.Fmt_ud(1<<63).Putnl();  // print string with automatic newline
-*/
 
  const nByte = 64;
  const nPows10 = 160;
@@ -33,6 +25,19 @@ func init() {
  	}
  }
  
+/*
+	Fmt is the raw formatter used by Printf etc.  Not meant for normal use.
+	See print.go for a more palatable interface.
+
+	Model is to accumulate operands into an internal buffer and then
+	retrieve the buffer in one hit using Str(), Putnl(), etc.  The formatting
+	methods return ``self'' so the operations can be chained.
+
+	f := fmt.New();
+	print f.Fmt_d(1234).Fmt_s("\n").Str();  // create string, print it
+	f.Fmt_d(-1234).Fmt_s("\n").Put();  // print string
+	f.Fmt_ud(1<<63).Putnl();  // print string with automatic newline
+*/
  type Fmt struct {
  	buf string;
  	wid int;
@@ -68,12 +73,14 @@ func (f *Fmt) init() {
  	f.clearflags();
  }
  
+// New returns a new initialized Fmt
  func New() *Fmt {
  	f := new(Fmt);
  	f.init();
  	return f;
  }
  
+// Str returns the buffered contents as a string and resets the Fmt.
  func (f *Fmt) Str() string {
  	s := f.buf;
  	f.clearbuf();
@@ -82,18 +89,21 @@ func (f *Fmt) Str() string {
  	return s;
  }
  
+// Put writes the buffered contents to stdout and resets the Fmt.
  func (f *Fmt) Put() {
  	print(f.buf);
  	f.clearbuf();
  	f.clearflags();
  }
  
+// Putnl writes the buffered contents to stdout, followed by a newline, and resets the Fmt.
  func (f *Fmt) Putnl() {
  	print(f.buf, "\n");
  	f.clearbuf();
  	f.clearflags();
  }
  
+// Wp sets the width and precision for formatting the next item.
  func (f *Fmt) Wp(w, p int) *Fmt {
  	f.wid_present = true;
  	f.wid = w;
@@ -102,12 +112,14 @@ func (f *Fmt) Wp(w, p int) *Fmt {
  	return f;
  }
  
+// P sets the precision for formatting the next item.
  func (f *Fmt) P(p int) *Fmt {
  	f.prec_present = true;
  	f.prec = p;
  	return f;
  }
  
+// W sets the width for formatting the next item.
  func (f *Fmt) W(x int) *Fmt {
  	f.wid_present = true;
  	f.wid = x;
@@ -162,9 +174,9 @@ func putint(buf *[nByte]byte, i int, base, val uint64, digits *string) int {
  	return i-1;
  }
  
-// boolean
-func (f *Fmt) Fmt_boolean(a bool) *Fmt {
-	if a {
+// Fmt_boolean formats a boolean.
+func (f *Fmt) Fmt_boolean(v bool) *Fmt {
+	if v {
  	f.pad("true");
  	} else {
  	f.pad("false");
@@ -213,152 +225,167 @@ func (f *Fmt) integer(a int64, base uint, is_signed bool, digits *string) string
  	return string(buf)[i+1:nByte];
  }
  
-// decimal
-func (f *Fmt) Fmt_d64(a int64) *Fmt {
-	f.pad(f.integer(a, 10, true, &ldigits));
+// Fmt_d64 formats an int64 in decimal.
+func (f *Fmt) Fmt_d64(v int64) *Fmt {
+	f.pad(f.integer(v, 10, true, &ldigits));
  	f.clearflags();
  	return f;
  }
  
-func (f *Fmt) Fmt_d32(a int32) *Fmt {
-	return f.Fmt_d64(int64(a));
+// Fmt_d32 formats an int32 in decimal.
+func (f *Fmt) Fmt_d32(v int32) *Fmt {
+	return f.Fmt_d64(int64(v));
  }
  
-func (f *Fmt) Fmt_d(a int) *Fmt {
-	return f.Fmt_d64(int64(a));
+// Fmt_d formats an int in decimal.
+func (f *Fmt) Fmt_d(v int) *Fmt {
+	return f.Fmt_d64(int64(v));
  }
  
-// unsigned Fmt_decimal
-func (f *Fmt) Fmt_ud64(a uint64) *Fmt {
-	f.pad(f.integer(int64(a), 10, false, &ldigits));
+// Fmt_ud64 formats a uint64 in decimal.
+func (f *Fmt) Fmt_ud64(v uint64) *Fmt {
+	f.pad(f.integer(int64(v), 10, false, &ldigits));
  	f.clearflags();
  	return f;
  }
  
-func (f *Fmt) Fmt_ud32(a uint32) *Fmt {
-	return f.Fmt_ud64(uint64(a));
+// Fmt_ud32 formats a uint32 in decimal.
+func (f *Fmt) Fmt_ud32(v uint32) *Fmt {
+	return f.Fmt_ud64(uint64(v));
  }
  
-func (f *Fmt) Fmt_ud(a uint) *Fmt {
-	return f.Fmt_ud64(uint64(a));
+// Fmt_ud formats a uint in decimal.
+func (f *Fmt) Fmt_ud(v uint) *Fmt {
+	return f.Fmt_ud64(uint64(v));
  }
  
-// hexdecimal
-func (f *Fmt) Fmt_x64(a int64) *Fmt {
-	f.pad(f.integer(a, 16, true, &ldigits));
+// Fmt_x64 formats an int64 in hexadecimal.
+func (f *Fmt) Fmt_x64(v int64) *Fmt {
+	f.pad(f.integer(v, 16, true, &ldigits));
  	f.clearflags();
  	return f;
  }
  
-func (f *Fmt) Fmt_x32(a int32) *Fmt {
-	return f.Fmt_x64(int64(a));
+// Fmt_x32 formats an int32 in hexadecimal.
+func (f *Fmt) Fmt_x32(v int32) *Fmt {
+	return f.Fmt_x64(int64(v));
  }
  
-func (f *Fmt) Fmt_x(a int) *Fmt {
-	return f.Fmt_x64(int64(a));
+// Fmt_x formats an int in hexadecimal.
+func (f *Fmt) Fmt_x(v int) *Fmt {
+	return f.Fmt_x64(int64(v));
  }
  
-// unsigned hexdecimal
-func (f *Fmt) Fmt_ux64(a uint64) *Fmt {
-	f.pad(f.integer(int64(a), 16, false, &ldigits));
+// Fmt_ux64 formats a uint64 in hexadecimal.
+func (f *Fmt) Fmt_ux64(v uint64) *Fmt {
+	f.pad(f.integer(int64(v), 16, false, &ldigits));
  	f.clearflags();
  	return f;
  }
  
-func (f *Fmt) Fmt_ux32(a uint32) *Fmt {
-	return f.Fmt_ux64(uint64(a));
+// Fmt_ux32 formats a uint32 in hexadecimal.
+func (f *Fmt) Fmt_ux32(v uint32) *Fmt {
+	return f.Fmt_ux64(uint64(v));
  }
  
-func (f *Fmt) Fmt_ux(a uint) *Fmt {
-	return f.Fmt_ux64(uint64(a));
+// Fmt_ux formats a uint in hexadecimal.
+func (f *Fmt) Fmt_ux(v uint) *Fmt {
+	return f.Fmt_ux64(uint64(v));
  }
  
-// HEXADECIMAL
-func (f *Fmt) Fmt_X64(a int64) *Fmt {
-	f.pad(f.integer(a, 16, true, &udigits));
+// Fmt_X64 formats an int64 in upper case hexadecimal.
+func (f *Fmt) Fmt_X64(v int64) *Fmt {
+	f.pad(f.integer(v, 16, true, &udigits));
  	f.clearflags();
  	return f;
  }
  
-func (f *Fmt) Fmt_X32(a int32) *Fmt {
-	return f.Fmt_X64(int64(a));
+// Fmt_X32 formats an int32 in upper case hexadecimal.
+func (f *Fmt) Fmt_X32(v int32) *Fmt {
+	return f.Fmt_X64(int64(v));
  }
  
-func (f *Fmt) Fmt_X(a int) *Fmt {
-	return f.Fmt_X64(int64(a));
+// Fmt_X formats an int in upper case hexadecimal.
+func (f *Fmt) Fmt_X(v int) *Fmt {
+	return f.Fmt_X64(int64(v));
  }
  
-// unsigned HEXADECIMAL
-func (f *Fmt) Fmt_uX64(a uint64) *Fmt {
-	f.pad(f.integer(int64(a), 16, false, &udigits));
+// Fmt_uX64 formats a uint64 in upper case hexadecimal.
+func (f *Fmt) Fmt_uX64(v uint64) *Fmt {
+	f.pad(f.integer(int64(v), 16, false, &udigits));
  	f.clearflags();
  	return f;
  }
  
-func (f *Fmt) Fmt_uX32(a uint32) *Fmt {
-	return f.Fmt_uX64(uint64(a));
+// Fmt_uX32 formats a uint32 in upper case hexadecimal.
+func (f *Fmt) Fmt_uX32(v uint32) *Fmt {
+	return f.Fmt_uX64(uint64(v));
  }
  
-func (f *Fmt) Fmt_uX(a uint) *Fmt {
-	return f.Fmt_uX64(uint64(a));
+// Fmt_uX formats a uint in upper case hexadecimal.
+func (f *Fmt) Fmt_uX(v uint) *Fmt {
+	return f.Fmt_uX64(uint64(v));
  }
  
-// octal
-func (f *Fmt) Fmt_o64(a int64) *Fmt {
-	f.pad(f.integer(a, 8, true, &ldigits));
+// Fmt_o64 formats an int64 in octal.
+func (f *Fmt) Fmt_o64(v int64) *Fmt {
+	f.pad(f.integer(v, 8, true, &ldigits));
  	f.clearflags();
  	return f;
  }
  
-func (f *Fmt) Fmt_o32(a int32) *Fmt {
-	return f.Fmt_o64(int64(a));
+// Fmt_o32 formats an int32 in octal.
+func (f *Fmt) Fmt_o32(v int32) *Fmt {
+	return f.Fmt_o64(int64(v));
  }
  
-func (f *Fmt) Fmt_o(a int) *Fmt {
-	return f.Fmt_o64(int64(a));
+// Fmt_o formats an int in octal.
+func (f *Fmt) Fmt_o(v int) *Fmt {
+	return f.Fmt_o64(int64(v));
  }
  
-
-// unsigned octal
-func (f *Fmt) Fmt_uo64(a uint64) *Fmt {
-	f.pad(f.integer(int64(a), 8, false, &ldigits));
+// Fmt_uo64 formats a uint64 in octal.
+func (f *Fmt) Fmt_uo64(v uint64) *Fmt {
+	f.pad(f.integer(int64(v), 8, false, &ldigits));
  	f.clearflags();
  	return f;
  }
  
-func (f *Fmt) Fmt_uo32(a uint32) *Fmt {
-	return f.Fmt_uo64(uint64(a));
+// Fmt_uo32 formats a uint32 in octal.
+func (f *Fmt) Fmt_uo32(v uint32) *Fmt {
+	return f.Fmt_uo64(uint64(v));
  }
  
-func (f *Fmt) Fmt_uo(a uint) *Fmt {
-	return f.Fmt_uo64(uint64(a));
+// Fmt_uo formats a uint in octal.
+func (f *Fmt) Fmt_uo(v uint) *Fmt {
+	return f.Fmt_uo64(uint64(v));
  }
  
-
-// unsigned binary
-func (f *Fmt) Fmt_b64(a uint64) *Fmt {
-	f.pad(f.integer(int64(a), 2, false, &ldigits));
+// Fmt_b64 formats a uint64 in binary.
+func (f *Fmt) Fmt_b64(v uint64) *Fmt {
+	f.pad(f.integer(int64(v), 2, false, &ldigits));
  	f.clearflags();
  	return f;
  }
  
-func (f *Fmt) Fmt_b32(a uint32) *Fmt {
-	return f.Fmt_b64(uint64(a));
+// Fmt_b32 formats a uint32 in binary.
+func (f *Fmt) Fmt_b32(v uint32) *Fmt {
+	return f.Fmt_b64(uint64(v));
  }
  
-func (f *Fmt) Fmt_b(a uint) *Fmt {
-	return f.Fmt_b64(uint64(a));
+// Fmt_b formats a uint in binary.
+func (f *Fmt) Fmt_b(v uint) *Fmt {
+	return f.Fmt_b64(uint64(v));
  }
  
-
-// character
-func (f *Fmt) Fmt_c(a int) *Fmt {
-	f.pad(string(a));
+// Fmt_c formats a Unicode character.
+func (f *Fmt) Fmt_c(v int) *Fmt {
+	f.pad(string(v));
  	f.clearflags();
  	return f;
  }
  
-// string
+// Fmt_s formats a string.
  func (f *Fmt) Fmt_s(s string) *Fmt {
  	if f.prec_present {
  		if f.prec < len(s) {
@@ -370,7 +397,7 @@ func (f *Fmt) Fmt_s(s string) *Fmt {
  	return f;
  }
  
-// hexadecimal string
+// Fmt_sx formats a string as a hexadecimal encoding of its bytes.
  func (f *Fmt) Fmt_sx(s string) *Fmt {
  	t := "";
  	for i := 0; i < len(s); i++ {
@@ -386,6 +413,7 @@ func (f *Fmt) Fmt_sx(s string) *Fmt {
  	return f;
  }
  
+// Fmt_sX formats a string as an uppercase hexadecimal encoding of its bytes.
  func (f *Fmt) Fmt_sX(s string) *Fmt {
  	t := "";
  	for i := 0; i < len(s); i++ {
@@ -398,7 +426,7 @@ func (f *Fmt) Fmt_sX(s string) *Fmt {
  	return f;
  }
  
-// quoted string
+// Fmt_q formats a string as a double-quoted, escaped Go string constant.
  func (f *Fmt) Fmt_q(s string) *Fmt {
  	var quoted string;
  	if f.sharp && strconv.CanBackquote(s) {
@@ -426,40 +454,48 @@ func fmtString(f *Fmt, s string) *Fmt {
  	return f;
  }
  
-// float64
-func (f *Fmt) Fmt_e64(a float64) *Fmt {
-	return fmtString(f, strconv.Ftoa64(a, 'e', doPrec(f, 6)));
+// Fmt_e64 formats a float64 in the form -1.23e+12.
+func (f *Fmt) Fmt_e64(v float64) *Fmt {
+	return fmtString(f, strconv.Ftoa64(v, 'e', doPrec(f, 6)));
  }
  
-func (f *Fmt) Fmt_f64(a float64) *Fmt {
-	return fmtString(f, strconv.Ftoa64(a, 'f', doPrec(f, 6)));
+// Fmt_f64 formats a float64 in the form -1.23.
+func (f *Fmt) Fmt_f64(v float64) *Fmt {
+	return fmtString(f, strconv.Ftoa64(v, 'f', doPrec(f, 6)));
  }
  
-func (f *Fmt) Fmt_g64(a float64) *Fmt {
-	return fmtString(f, strconv.Ftoa64(a, 'g', doPrec(f, -1)));
+// Fmt_g64 formats a float64 in the 'f' or 'e' form according to size.
+func (f *Fmt) Fmt_g64(v float64) *Fmt {
+	return fmtString(f, strconv.Ftoa64(v, 'g', doPrec(f, -1)));
  }
  
-func (f *Fmt) Fmt_fb64(a float64) *Fmt {
-	return fmtString(f, strconv.Ftoa64(a, 'b', 0));
+// Fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2).
+func (f *Fmt) Fmt_fb64(v float64) *Fmt {
+	return fmtString(f, strconv.Ftoa64(v, 'b', 0));
  }
  
  // float32
  // cannot defer to float64 versions
  // because it will get rounding wrong in corner cases.
-func (f *Fmt) Fmt_e32(a float32) *Fmt {
-	return fmtString(f, strconv.Ftoa32(a, 'e', doPrec(f, 6)));
+//
+// Fmt_e32 formats a float32 in the form -1.23e+12.
+func (f *Fmt) Fmt_e32(v float32) *Fmt {
+	return fmtString(f, strconv.Ftoa32(v, 'e', doPrec(f, 6)));
  }
  
-func (f *Fmt) Fmt_f32(a float32) *Fmt {
-	return fmtString(f, strconv.Ftoa32(a, 'f', doPrec(f, 6)));
+// Fmt_f32 formats a float32 in the form -1.23.
+func (f *Fmt) Fmt_f32(v float32) *Fmt {
+	return fmtString(f, strconv.Ftoa32(v, 'f', doPrec(f, 6)));
  }
  
-func (f *Fmt) Fmt_g32(a float32) *Fmt {
-	return fmtString(f, strconv.Ftoa32(a, 'g', doPrec(f, -1)));
+// Fmt_g32 formats a float32 in the 'f' or 'e' form according to size.
+func (f *Fmt) Fmt_g32(v float32) *Fmt {
+	return fmtString(f, strconv.Ftoa32(v, 'g', doPrec(f, -1)));
  }
  
-func (f *Fmt) Fmt_fb32(a float32) *Fmt {
-	return fmtString(f, strconv.Ftoa32(a, 'b', 0));
+// Fmt_fb32 formats a float32 in the form -123p3 (exponent is power of 2).
+func (f *Fmt) Fmt_fb32(v float32) *Fmt {
+	return fmtString(f, strconv.Ftoa32(v, 'b', 0));
  }
  
  // float

src/lib/fmt/print.go

--- a/src/lib/fmt/print.go
+++ b/src/lib/fmt/print.go
@@ -2,12 +2,21 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// Package fmt implements formatted I/O with functions analogous
+// to C's printf.  Because of reflection knowledge it does not need
+// to be told about sizes and signedness (no %llud etc. - just %d).
+// Still to do: document the formats properly.  For now, like C but:
+//	- don't need l or u flags - type of integer tells that.
+//	- %v prints any value using its native format.
+//	- for each Printf-like fn, there is also a Print fn that takes no format
+//	  and is equivalent to saying %v for every operand.
+//	- another variant Println inserts blanks and appends a newline.
+//	- if an operand implements method String() that method will
+//	  be used for %v, %s, or Print etc.
+//	- if an operand implements interface Formatter, that interface can
+//	  be used for fine control of formatting.
  package fmt
  
-/*
-	C-like printf, but because of reflection knowledge does not need
-	to be told about sizes and signedness (no %llud etc. - just %d).
-*/
 
  import (
  	"fmt";
@@ -17,27 +26,37 @@ import (
  	"utf8";
  )
  
-// Representation of printer state passed to custom formatters.
-// Provides access to the io.Write interface plus information about
-// the active formatting verb.
+// Formatter represents the printer state passed to custom formatters.
+// It provides access to the io.Write interface plus information about
+// the flags and options for the operand's format specifier.
  type Formatter interface {
+\t// Write is the function to call to emit formatted output to be printed.
  	Write(b []byte) (ret int, err *os.Error);
+\t// Width returns the value of the width option and whether it has been set.
  	Width()	(wid int, ok bool);
+\t// Precision returns the value of the precision option and whether it has been set.
  	Precision()	(prec int, ok bool);
  
-\t// flags
+\t// Flag returns whether the flag c, a character, has been set.
  	Flag(int)	bool;
  }\n 
+// Format is the interface implemented by objects with a custom formatter.
+// The implementation of Format may call Sprintf or Fprintf(f) etc.
+// to generate its output.
  type Format interface {
  	Format(f Formatter, c int);
  }\n 
+// String represents any object being printed that has a String() method that
+// returns a string, which defines the ``native'' format for that object.
+// Any such object will be printed using that method if passed
+// as operand to a %s or %v format or to an unformatted printer such as Print.
  type String interface {
  	String() string
  }\n 
-const runeSelf = 0x80
+const runeSelf = utf8.RuneSelf
  const allocSize = 32
  
  type pp struct {
@@ -129,6 +148,7 @@ func (p *pp) doprint(v reflect.StructValue, addspace, addnewline bool);
  
  // These routines end in 'f' and take a format string.\n 
+// Fprintf formats according to a format specifier and writes to w.
  func Fprintf(w io.Write, format string, a ...) (n int, error *os.Error) {
  	v := reflect.NewValue(a).(reflect.StructValue);
  	p := newPrinter();
@@ -137,11 +157,13 @@ func Fprintf(w io.Write, format string, a ...) (n int, error *os.Error) {
  	return n, error;
  }\n 
+// Printf formats according to a format specifier and writes to standard output.
  func Printf(format string, v ...) (n int, errno *os.Error) {
  	n, errno = Fprintf(os.Stdout, format, v);
  	return n, errno;
  }\n 
+// Sprintf formats according to a format specifier and returns the resulting string.
  func Sprintf(format string, a ...) string {
  	v := reflect.NewValue(a).(reflect.StructValue);
  	p := newPrinter();
@@ -150,9 +172,10 @@ func Sprintf(format string, a ...) string {
  	return s;
  }\n 
-// These routines do not take a format string and add spaces only
-// when the operand on neither side is a string.
+// These routines do not take a format string
  
+// Fprint formats using the default formats for its operands and writes to w.
+// Spaces are added between operands when neither is a string.
  func Fprint(w io.Write, a ...) (n int, error *os.Error) {
  	v := reflect.NewValue(a).(reflect.StructValue);
  	p := newPrinter();
@@ -161,11 +184,15 @@ func Fprint(w io.Write, a ...) (n int, error *os.Error) {
  	return n, error;
  }\n 
+// Print formats using the default formats for its operands and writes to standard output.
+// Spaces are added between operands when neither is a string.
  func Print(v ...) (n int, errno *os.Error) {
  	n, errno = Fprint(os.Stdout, v);
  	return n, errno;
  }\n 
+// Sprint formats using the default formats for its operands and returns the resulting string.
+// Spaces are added between operands when neither is a string.
  func Sprint(a ...) string {
  	v := reflect.NewValue(a).(reflect.StructValue);
  	p := newPrinter();
@@ -178,6 +205,8 @@ func Sprint(a ...) string {
  // always add spaces between operands, and add a newline
  // after the last operand.\n 
+// Fprintln formats using the default formats for its operands and writes to w.
+// Spaces are always added between operands and a newline is appended.
  func Fprintln(w io.Write, a ...) (n int, error *os.Error) {
  	v := reflect.NewValue(a).(reflect.StructValue);
  	p := newPrinter();
@@ -186,11 +215,15 @@ func Fprintln(w io.Write, a ...) (n int, error *os.Error) {
  	return n, error;
  }\n 
+// Println formats using the default formats for its operands and writes to standard output.
+// Spaces are always added between operands and a newline is appended.
  func Println(v ...) (n int, errno *os.Error) {
  	n, errno = Fprintln(os.Stdout, v);
  	return n, errno;
  }\n 
+// Sprintln formats using the default formats for its operands and returns the resulting string.
+// Spaces are always added between operands and a newline is appended.
  func Sprintln(a ...) string {
  	v := reflect.NewValue(a).(reflect.StructValue);
  	p := newPrinter();
@@ -596,6 +629,13 @@ func (p *pp) doprintf(format string, v reflect.StructValue) {
  
  			// string
  			case 's':
+\t\t\t\tif inter != nil {
+\t\t\t\t\t// if object implements String, use the result.
+\t\t\t\t\tif stringer, ok := inter.(String); ok {
+\t\t\t\t\t\ts = p.fmt.Fmt_s(stringer.String()).Str();
+\t\t\t\t\t\tbreak;
+\t\t\t\t\t}
+\t\t\t\t}
  			if v, ok := getString(field); ok {
  				s = p.fmt.Fmt_s(v).Str()
  			} else {

コアとなるコードの解説

src/lib/fmt/format.go

このファイルは、fmtパッケージの低レベルなフォーマット処理を担うFmt構造体とそのメソッドを定義しています。変更のほとんどは、既存の関数や構造体に対する詳細なコメントの追加です。

  • Fmt構造体のコメント: FmtPrintfなどの高レベルな関数によって内部的に使用される「生のフォーマッタ」であることが強調されています。これは、開発者が直接Fmtを操作するのではなく、fmt.Printfなどのより使いやすいインターフェースを利用すべきであることを示唆しています。また、Fmtがどのように文字列をバッファリングし、メソッドチェーンを可能にするかという内部的な動作モデルも説明されています。
  • Fmtメソッドのコメント: New()Str()Put()Putnl()Wp()P()W()といった基本的な操作を提供するメソッドに、その機能が簡潔に説明されています。これにより、各メソッドの役割が一目でわかるようになります。
  • Fmt_プレフィックスを持つフォーマット関数のコメントと引数名変更: Fmt_d64(10進数)、Fmt_x64(16進数)、Fmt_o64(8進数)、Fmt_b64(2進数)、Fmt_c(文字)、Fmt_s(文字列)、Fmt_e64(指数表記浮動小数点数)など、様々な型と基数に対応するフォーマット関数にコメントが追加されました。これらのコメントは、各関数がどのような値をどのような形式でフォーマットするかを明確にしています。また、引数名がaからvに変更されたことで、引数が「値」(value)であることをより直感的に示しています。

これらの変更は、fmtパッケージの内部構造を理解しようとする開発者にとって、非常に有用な情報を提供します。

src/lib/fmt/print.go

このファイルは、fmtパッケージのユーザー向けの高レベルなインターフェース(PrintfPrintなど)を定義しています。

  • パッケージレベルのコメント: fmtパッケージ全体の概要が詳細に記述されています。C言語のprintfとの比較、Goのリフレクションによる型推論の利点、%v動詞の汎用性、Print/Println関数のバリアント、そしてString()メソッドやFormatterインターフェースによるカスタムフォーマットのサポートが説明されています。これは、fmtパッケージの設計思想と主要な機能を知る上で不可欠な情報です。
  • FormatterFormatStringインターフェースのコメント: これらのインターフェースは、カスタム型がfmtパッケージと連携して独自のフォーマットロジックを提供するための重要なメカニズムです。それぞれのインターフェースの目的と、それが提供するメソッド(Write, Width, Precision, Flag for Formatter)が明確に説明されています。特にStringインターフェースについては、%s%v、あるいは非フォーマット出力(Printなど)でどのように利用されるかが詳細に記述されており、カスタム型の文字列表現を制御する上での重要な指針となります。
  • runeSelf定数の変更: 0x80というマジックナンバーがutf8.RuneSelfという定数に置き換えられました。これは、コードの意図をより明確にし、将来的な変更に対する堅牢性を高めるための良いプラクティスです。
  • PrintfPrintPrintln系関数のコメント: FprintfPrintfSprintfFprintPrintSprintFprintlnPrintlnSprintlnといった、ユーザーが最も頻繁に利用する関数群に、その機能と出力の振る舞いを説明するコメントが追加されました。これにより、各関数の使い分けが容易になります。
  • doprintf関数内の%sフォーマット処理の改善:
    • この変更は、fmtパッケージの動作に直接影響を与える重要な機能改善です。以前は、%sフォーマット動詞が文字列型以外の引数に対して使用された場合、その引数がString()メソッドを実装していても、そのメソッドが呼び出されない可能性がありました。
    • 追加されたコードは、%sフォーマット動詞が適用されるオブジェクトがStringインターフェースを実装しているかどうかをチェックし、もし実装していれば、そのString()メソッドの戻り値を使用して文字列をフォーマットするように変更しています。
    • これにより、開発者はカスタム型にString()メソッドを実装するだけで、fmt.Printf("%s", myCustomObject)fmt.Print(myCustomObject)のように、そのオブジェクトを自然な形で文字列として出力できるようになりました。これは、Go言語における慣用的な文字列表現のメカニズムをfmtパッケージが適切にサポートするための重要なステップです。

これらの変更は、fmtパッケージのドキュメンテーションを大幅に強化し、その機能と利用方法をより明確にすることで、Go言語の使いやすさと理解度を向上させることに貢献しています。

関連リンク

参考にした情報源リンク