ユーザー定義型が
String() string
メソッドを実装している場合、fmt関数(例えば、fmt.Println、fmt.Printf("%v")、logパッケージ)でこの値を使用すると、文字列表現を取得するためにこのメソッドが呼ばれます。
ユーザー定義のエンティティ型について、意味のある人間が読める出力を得るためにString()を実装することが推奨されます。標準のfmt出力(%v)ではなく、コンテキストに適した簡潔な出力を行い、可能であれば安全であること(機密データの漏洩を防ぐ)を心がけるべきです。
実装例:
type Order struct { ID string Amount int } func (o Order) String() string { return fmt.Sprintf("Order[%s]: %d", o.ID, o.Amount) } func main() { o := Order{"A123", 99} fmt.Println(o) } // 出力: Order[A123]: 99
メソッドが誤って実装されている場合や適切でない場合は、意味のないまたは危険な出力を伴うログが生成され、バグの発見が難しくなります。
もしあなたの型がString()メソッドを実装しているが、値ではなくポインタとして使用している場合、String()は呼ばれますか?
回答: ポインタ型にString()メソッドが実装されていれば呼ばれます。値に対してのみ実装されているがコード上で型がポインタである場合、Goがポインタレシーバーのないメソッドに対して自動デリファレンスを行うため、メソッドが呼ばれます。通常は問題ありませんが、メソッドがポインタレシーバーにのみ実装されていて、値に対して呼び出す場合はコンパイルエラーになります。
例:
type X struct{} func (x *X) String() string { return "ptr" } fmt.Println(X{}) // エラー: XはStringを実装していません fmt.Println(&X{}) // ok, メソッドが呼ばれる
物語
大規模プロジェクトで、エラー型はString()をポインタレシーバーでのみ実装していました(
func (err *MyErr) String() string)。そのため、ログでエラー値(ポインタではない)を返す際に、役に立たない{}が出力され、役立つメッセージが表示されませんでした。このバグは長い間見逃されました。
物語
ある型は機密データを保持しており、String()の誤った実装により(フィールドを明示的に出力)、ユーザーパスワードがプロダクションログに表示され始めました。セキュリティ監査が必要になりました。
物語
String()を自動生成する使用が、構造体の更新時に新しい出力形式が以前と一致せず、ログが読み取れなくなり、外部システムによるログのパースに支障をきたしました。