fmt.Println による構造体の出力をカスタマイズする (Stringer)

Golang での構造体の出力

Golang の構造体の内容は、fmt パッケージの各種関数で出力できるようになっています。 次のコードでは、Game 構造体の内容をいくつかの方法で出力しています。

package main

import "fmt"

type Game struct {
	Title string
	Price int
}

func main() {
	g := &Game{Title: "ドンキーコング", Price: 4500}

	fmt.Println(g)         // &{ドンキーコング 4500}
	fmt.Printf("%v\n", g)  // &{ドンキーコング 4500}
	fmt.Printf("%+v\n", g) // &{Title:ドンキーコング Price:4500}
	fmt.Printf("%#v\n", g) // &main.Game{Title:"ドンキーコング", Price:4500}
}

こういった出力で十分であればよいのですが、異なる出力が欲しいときは、次に説明する Stringer インタフェースを実装することで対応できます。

String 関数による出力のカスタマイズ

fmt パッケージで構造体を文字列表現に変換するとき、内部で String 関数が呼び出されます。 String 関数は、fmt パッケージの Stringer インタフェースとして定義されています。

type Stringer interface {
    String() string
}

例えば、Game 構造体の出力をカスタマイズしたいときは、次のように String() 関数を実装します。

type Game struct {
	Title string
	Price int
}

// Game 構造体の文字列表現
func (g Game) String() string {
	return fmt.Sprintf("「%s」 %d円", g.Title, g.Price)
}

すると、fmt パッケージの各種関数による出力は次のように変化します。

func main() {
	g := &Game{Title: "ドンキーコング", Price: 4500}
	fmt.Println(g)         // 「ドンキーコング」 4500円
	fmt.Printf("%v\n", g)  // 「ドンキーコング」 4500円
	fmt.Printf("%+v\n", g) // 「ドンキーコング」 4500円
	fmt.Printf("%#v\n", g) // &main.Game{Title:"ドンキーコング", Price:4500}
}

Golang のソースコード表現を表す %#v の出力だけは変化しないようです。

構造体の内容をきれいな JSON 形式で出力したければ、json パッケージを使って次のように実装できます(参考: Golang で JSON を扱う)。

func (g Game) String() string {
	bytes, err := json.MarshalIndent(g, "", "  ")
	if err != nil {
		// Cyclic data structures が見つかった時の対策
		return g.Title
	}
	return string(bytes)
}
出力例
{
  "Title": "ドンキーコング",
  "Price": 4500
}

ただ、デバッグ用途の出力であれば、素直に出力専用の関数を定義した方がよさそうです。

func PrintGame(g *Game) {
	bytes, _ := json.MarshalIndent(g, "", "  ")
	fmt.Println(string(bytes))
}