ホームページ  >  記事  >  バックエンド開発  >  Go の `fmt.Stringer` でレシーバーとして `t` と `*t` を使用するとデッド ループが発生するのはなぜですか?

Go の `fmt.Stringer` でレシーバーとして `t` と `*t` を使用するとデッド ループが発生するのはなぜですか?

Patricia Arquette
Patricia Arquetteオリジナル
2024-11-22 22:49:12692ブラウズ

Why does using `t` vs `*t` as a receiver in Go's `fmt.Stringer` lead to a dead loop?

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 サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。