t と *t の違いを理解する: fmt パッケージのケーススタディ
Go では、fmt パッケージは強力な書式設定機能を提供しますを使用すると、開発者は値の出力方法をカスタマイズできます。ただし、t と *t の微妙な違いにより、予期しない動作、つまり受信側メソッド内で使用するとデッド ループが発生する可能性があります。
次のコード スニペットを考えてみましょう。
package main import "fmt" type TT struct { a int b float32 c string } func (t *TT) String() string { return fmt.Sprintf("%+v", *t) } func main() { tt := &TT{3, 4, "5"} fmt.Printf(tt.String()) }
このコードは正常に実行され、「tt」変数の内容が「tt={a:3, b:4, c:5}」として出力されます。ただし、String メソッドを次のように変更すると、
func (t *TT) String() string { return fmt.Sprintf("%+v", t) }
デッド ループが発生します。その理由は、fmt パッケージが String() メソッドを実装する (fmt.Stringer インターフェイスを実装する) 値を処理する方法にあります。
「tt」の値が fmt.Println に渡されると、fmt パッケージはTT 型が String() メソッドを実装しているかどうかを確認します。 String() メソッドをポインタ レシーバ (*TT) で定義したため、TT に設定されたメソッドには String() が含まれません。その結果、fmt パッケージは tt.String() を呼び出しません。
一方、変更されたコード スニペットでは、*TT のレシーバー タイプで String() メソッドを定義します。この場合、TT に設定されるメソッドには String() が含まれます。ただし、*TT 型の tt を渡しているため、 tt.String() を呼び出すと基本的に String() メソッドが再帰的に呼び出され、無限ループが発生します。
この問題を回避するには、次のようにすることをお勧めします。 String() メソッドには別のレシーバー タイプを使用してください。 1 つのアプローチは、type キーワードを使用して新しい型を作成し、渡される値に対して型変換を実行することです。これにより、メソッドのない新しい型が作成され、無限ループが回避されます。
例:
func (t TT) String() string { type TT2 TT return fmt.Sprintf("%+v", TT2(t)) }
この場合、t を TT2 に変換すると、String() メソッドが効果的に変換されなくなります。 TT2 には String() メソッドがないため、再帰的に呼び出すことができます。
のコンテキストにおける t と *t の違いを理解するfmt パッケージは、予期しない動作を回避し、効率的なコード実行を保証するために非常に重要です。
以上がGo の `fmt.Stringer` でレシーバーとして `t` と `*t` を使用するとデッド ループが発生するのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。