Goで符号なし整数型を書式指定子で出力する際に注意すること
はじめに
ある日、符号なし整数型で宣言した値に関わるエラー処理部において、ロギングのためにその値を出力させたかった。
値は後述のような構造体内で保持していたので、書式指定子%#v
を用いたのだが、ログに吐かれた値は期待した10進数でなく、16進数になってしまう。理由が分かってなかったので調べてみた。
実行
サンプルコード
package main import ( "fmt" ) type User struct { ID uint } func main() { users := make([]User,0) users = append(users, User{ID: 1111}, User{ID: 2222}) fmt.Printf("users[%%+v] -> %+v\n", users) fmt.Printf("users[%%#v] -> %#v\n", users) }
実行結果
users[%+v] -> [{ID:1111} {ID:2222}] users[%#v] -> []main.User{main.User{ID:0x457}, main.User{ID:0x8ae}}
仕様を読む
Go言語のfmt
パッケージ仕様を確認する。
%v the value in a default format when printing structs, the plus flag (%+v) adds field names
%#v a Go-syntax representation of the value
今回のようにスライスを出力する場合,+
フラグ を付与した書式識別子%+v
を使うことで、デフォルトフォーマットに加えて構造体のフィールド名も出力してくれる。ゆえにID
の値も10進数だ。
一方、書式識別子%#v
を用いると、スライス内のID
は16進数で出力された。
実装を追う
fmt
パッケージで提供されるprint系関数の実装を追う。
関係する箇所を下記に引用した。
// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x or // not, as requested, by temporarily setting the sharp flag. func (p *pp) fmt0x64(v uint64, leading0x bool) { sharp := p.fmt.sharp p.fmt.sharp = leading0x p.fmt.fmtInteger(v, 16, unsigned, 'v', ldigits) p.fmt.sharp = sharp } // fmtInteger formats a signed or unsigned integer. func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) { switch verb { case 'v': if p.fmt.sharpV && !isSigned { p.fmt0x64(v, true) } else { p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits) } (中略) }
書式識別子%#v
でunsigned integer(符号なし整数)、つまりuint型を出力する場合は16進数で出力する実装になっていたことが分かった。
まとめ
%#v
で符号なし整数型を扱う際は注意する。
というより、その場合は%+v
を使えば良さそうだ。