web-dev-qa-db-ja.com

ポインターとパラメーターおよび戻り値の値

Goでは、struct値またはそのスライスを返すさまざまな方法があります。私が見た個々のものについて:

type MyStruct struct {
    Val int
}

func myfunc() MyStruct {
    return MyStruct{Val: 1}
}

func myfunc() *MyStruct {
    return &MyStruct{}
}

func myfunc(s *MyStruct) {
    s.Val = 1
}

これらの違いを理解しています。最初は構造体のコピーを返し、2番目は関数内で作成された構造体値へのポインタを返し、3番目は既存の構造体が渡されることを期待して値をオーバーライドします。

私はこれらのパターンのすべてがさまざまなコンテキストで使用されているのを見てきましたが、これらに関するベストプラクティスは何なのでしょうか。どちらを使用しますか?たとえば、最初の構造体は小さな構造体(オーバーヘッドが最小であるため)で問題ありませんが、2番目の構造体は大きな構造体で問題ありません。 3番目は、呼び出し間で単一のstructインスタンスを簡単に再利用できるため、非常にメモリ効率を高めたい場合です。どちらを使用するかについてのベストプラクティスはありますか?

同様に、スライスに関する同じ質問:

func myfunc() []MyStruct {
    return []MyStruct{ MyStruct{Val: 1} }
}

func myfunc() []*MyStruct {
    return []MyStruct{ &MyStruct{Val: 1} }
}

func myfunc(s *[]MyStruct) {
    *s = []MyStruct{ MyStruct{Val: 1} }
}

func myfunc(s *[]*MyStruct) {
    *s = []MyStruct{ &MyStruct{Val: 1} }
}

繰り返しますが、ここでのベストプラクティスは何ですか。スライスは常にポインターであることを知っているので、スライスへのポインターを返すことは有用ではありません。ただし、構造体値のスライス、構造体へのポインタのスライスを返す必要がありますか、スライスへのポインタを引数として渡す必要があります( Go App Engine API で使用されるパターン)?

263
Zef Hemel

メソッドレシーバーをポインターとして使用する主な3つの理由:

  1. 「最初に、そして最も重要なのは、メソッドがレシーバを変更する必要があるかどうかです。変更する場合、レシーバはポインタでなければなりません。」

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

  3. 「次は一貫性です。型のメソッドの一部にポインターレシーバーが必要な場合、残りもそうである必要があるため、型の使用方法に関係なくメソッドセットは一貫性があります。」

リファレンス: https://golang.org/doc/faq#methods_on_values_or_pointers

編集:もう1つの重要なことは、関数に送信する実際の「タイプ」を知ることです。タイプは、「値タイプ」または「参照タイプ」のいずれかです。

スライスやマップが参照として機能する場合でも、関数のスライスの長さを変更するようなシナリオでは、それらをポインターとして渡したい場合があります。

8
Santosh Pillai

一般にポインターを返す必要があるのは、ステートフルまたは共有可能なリソースインスタンスを構築するときです。多くの場合、これは New が前に付いた関数によって行われます。

それらは何かの特定のインスタンスを表し、何らかのアクティビティを調整する必要がある場合があるため、同じリソースを表す複製/コピーされた構造を生成することはあまり意味がありません-したがって、返されるポインターはリソース自体へのハンドルとして機能します。

いくつかの例:

その他の場合、デフォルトでは構造が大きすぎてコピーできないため、ポインタが返されます。


あるいは、内部的にポインターを含む構造体のコピーを返すことにより、ポインターを直接返すことを避けることができますが、おそらくこれは慣用的とは見なされません:

0
nobar