13

あなたはいつもこれが欲しいようです:

func (self *Widget) Do() {
}

これの代わりに

func (self Widget) Do() {
}

もしそうなら、前者のセマンティクスを取得する方法は後者の構文を使用する必要があります。つまり、レシーバーは参照渡しである必要があります。

4

3 に答える 3

30

これは、Go のすべてが値渡しであるためです。これにより、他の C ファミリー言語との一貫性が保たれ、見ている状況が値渡しであるかどうかを覚えておく必要がまったくないことを意味します。

そのリンクから:

C ファミリーのすべての言語と同様に、Go のすべては値渡しです。つまり、関数は常に、値をパラメーターに代入する代入ステートメントがあるかのように、渡されるもののコピーを取得します。たとえば、関数に int 値を渡すと int のコピーが作成され、ポインター値を渡すとポインターのコピーが作成されますが、ポインターが指すデータは作成されません。(これがメソッドレシーバーに与える影響については、次のセクションを参照してください。)

じゃあ後で:

  func (s *MyStruct) pointerMethod() { } // method on pointer
  func (s MyStruct)  valueMethod()   { } // method on value

ポインターに慣れていないプログラマーにとって、これら 2 つの例の違いはわかりにくいかもしれませんが、状況は実際には非常に単純です。型でメソッドを定義する場合、レシーバー (s上記の例) は、メソッドの引数であるかのように動作します。レシーバーを値として定義するかポインターとして定義するかは、関数の引数を値とポインターのどちらにするかという問題と同じです。いくつかの考慮事項があります。

まず、最も重要なことは、メソッドがレシーバーを変更する必要があるかどうかです。その場合、レシーバーはポインターでなければなりません。(スライスとマップは参照として機能するため、その話はもう少し微妙ですが、たとえば、メソッド内のスライスの長さを変更するには、レシーバーは引き続きポインターでなければなりません。) 上記の例で、pointerMethod がのフィールドを変更する場合s、呼び出し元にはこれらの変更が表示されますが、valueMethod は呼び出し元の引数のコピー (値を渡す定義) で呼び出されるため、変更は呼び出し元には表示されません。

ところで、ポインター レシーバーは Java での状況と同じですが、Java ではポインターはカバーの下に隠されています。珍しいのは、Go の値レシーバーです。

2つ目は、効率性への配慮です。レシーバーが大きい場合、たとえば大きな構造体の場合、ポインター レシーバーを使用する方がはるかに安価です。

次は一貫性です。型のメソッドの一部にポインター レシーバーが必要な場合は、残りのメソッドも必要であるため、型の使用方法に関係なく、メソッド セットは一貫しています。詳細については、メソッド セットのセクションを参照してください。

基本型、スライス、小さな構造体などの型の場合、値レシーバーは非常に安価であるため、メソッドのセマンティクスがポインターを必要としない限り、値レシーバーは効率的で明確です。

于 2013-08-26T02:01:52.600 に答える
2

あなたはいつもこれが欲しいようです:

いいえ。値レシーバーはより一般的です。ポインターレシーバーが使用できるすべての場所で使用できます。ただし、値レシーバーが使用できるすべての場所でポインター レシーバーを使用できるわけではありません。たとえば、タイプWidget;の右辺値式がある場合などです。値レシーバー メソッドを呼び出すことはできますが、ポインター レシーバー メソッドを呼び出すことはできません。

于 2013-08-26T08:01:29.653 に答える