ホームページ  >  記事  >  バックエンド開発  >  ## ポインター レシーバー メソッドを使用して Go 型のインスタンスをコピーするのはなぜ危険ですか?

## ポインター レシーバー メソッドを使用して Go 型のインスタンスをコピーするのはなぜ危険ですか?

Linda Hamilton
Linda Hamiltonオリジナル
2024-10-25 10:54:30243ブラウズ

## Why is Copying Instances of Go Types with Pointer Receiver Methods Dangerous?

ポインター レシーバー メソッドを使用したインスタンスのコピーの落とし穴

Go では、名前付き型 T のすべてのメソッドが T 自体のレシーバー型を持つ場合(*T ではない)、そのタイプのインスタンスのコピーは安全であると考えられます。これは、メソッド呼び出しは必然的にコピーに対して動作し、元の値が変更されないことが保証されるためです。ただし、T のメソッドにポインター レシーバーがある場合、T のインスタンスをコピーすることは危険である可能性があります。

説明

メソッドを呼び出すときに、そのメソッドが呼び出される値on が最初にコピーされ、そのコピーがレシーバーとして渡されます。型に値レシーバーを持つメソッドのみがある場合、メソッドのアクションに関係なく、メソッドが元の値を変更できないことが保証されます。これは、コピーが常に利用され、元の値が意図しない変更から保護されるためです。

ただし、型にポインター レシーバーを持つメソッドがある場合、それらのメソッドはコピーではなく元の指す値を変更する可能性があります。これは、メソッドが元の値へのポインタを受け取り、基になるデータを変更できるためです。

ラッパー型 Wrapper:

type Wrapper struct {
    v int
    p *int
}

Set() メソッドを使用して、両方のフィールドに同じ値が含まれていることを確認します。

func (w *Wrapper) Set(v int) {
    w.v = v
    *w.p = v
}

Wrapper のインスタンスを作成した場合:

a := Wrapper{v: 0, p: new(int)}

その後、 a のコピー (b):

b := a

Set():

a.Set(1)

を使用して a を 1 に設定した後、a と b の両方のフィールドが 1 に設定されることが期待されます。ただし、それらの値を出力すると、別の話が明らかになります。

fmt.Printf("a.v=%d, a.p=%d;  b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)

出力:

a.v=1, a.p=1;  b.v=0, b.p=1

この不一致の理由は、b のポインターがコピーされている間、依然として同じものを参照していることです。基になるデータを a のポインターとして使用します。 Set() がポイントされた値を変更すると、Wrapper の両方のコピーに影響します。ただし、非ポインター フィールド v は、a と b の間で区別されます。

ベスト プラクティス

この問題を回避するには、次のような型のインスタンスをコピーしないことをお勧めします。ポインタ受信メソッド。ポインター値の操作が必要な場合は、ポインター自体 (*T) をコピーすることが実行可能な代替手段です。

以上が## ポインター レシーバー メソッドを使用して Go 型のインスタンスをコピーするのはなぜ危険ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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