GoでポインタのString()をしたときの興味深い結果
はじめに
標準出力する際に fmt.Print を用いることがあると思います。ポインタを fmt.Println に渡すと興味深い結果が表示されることがあります。その例を紹介します。
まずは以下の実装例を見てください。単に fmt.Println で time.Now() の変数とそのポインタ型の変数を標準出力しているだけです。
package main import ( "fmt" "time" ) func main() { t := time.Now() printTime(&t) } func printTime(t *time.Time) { fmt.Println(t) fmt.Println(*t) }
- 出力結果
上記の実装の出力結果は以下のようになります。ポインタの値を表示される想定でしたが、実際に表示されたのは time.Time の値でした。なぜでしょう。
2009-11-10 23:00:00 +0000 UTC m=+0.000000001 2009-11-10 23:00:00 +0000 UTC m=+0.000000001
https://play.golang.org/p/8_kqljv7LVL
理由
fmtが表示するもの
まず fmt.Println が何を表示するのか確認します。Package fmt によると %T
や %p
を用いる場合を除いて、指定の順番で表示するようになっています。 fmt.Println
に効いてくるのは 5 番目の内容です。
- If an operand implements method String() string, that method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).
String() メソッドを実装している場合は、オブジェクトを文字列に変換するために String() が呼ばれます。time.Time は以下のように String() メソッドを実装していました。そのため fmt.Print() で標準出力するときは String() メソッドの返り値の文字列が表示されます。
func (t Time) String() string { s := t.Format("2006-01-02 15:04:05.999999999 -0700 MST") // Format monotonic clock reading as m=±ddd.nnnnnnnnn. if t.wall&hasMonotonic != 0 { m2 := uint64(t.ext) sign := byte('+') if t.ext < 0 { sign = '-' m2 = -m2 } m1, m2 := m2/1e9, m2%1e9 m0, m1 := m1/1e9, m1%1e9 var buf []byte buf = append(buf, " m="...) buf = append(buf, sign) wid := 0 if m0 != 0 { buf = appendInt(buf, int(m0), 0) wid = 9 } buf = appendInt(buf, int(m1), wid) buf = append(buf, '.') buf = appendInt(buf, int(m2), 9) s += string(buf) } return s }
ポインタのメソッドセット
time.Time は String() メソッドを保持しているので文字列を表示するのは分かりました。ではなぜ *time.Time が String() メソッドを保持しているのでしょうか?
Method sets から引用すると、以下が理由になります。
The method set of the corresponding pointer type T is the set of all methods declared with receiver T or T (that is, it also contains the method set of T).
つまり T 型のメソッドセットは T 型と T 型のメソッドセットを保持します。
よって今回の例では *time.Time 型の変数は time.Time 型で実装していた String() を保持しているので String() が返却する文字列を表示することができました。