web-dev-qa-db-ja.com

Goでレシーバ変数に「自己」という名前を付けると、誤解を招く、または良い習慣になりますか?

私はGoでかなりの量のブログとビデオを見てきましたが、私が思い出す限り、メソッドを記述するときにレシーバー変数に「self」または「this」を使用している著者はいません。しかし、これを行うスタックオーバーフローにはいくつかの質問があるようで、これが変数に 'self'という名前を付けるのに誤解を招くのではないかと考えました。

メソッドセット の仕様を読んでも、(私の解釈では)どちらの証拠も提供されません。

私はどこかでそれが本当の意味でのセルフポインタではないことを見つけたことを思い出しているようです。誰かが証拠を挙げたり、推論を提供したりできますか。

簡単な例:

type MyStruct struct {
    Name string
} 

どちらの方法が適していますか?

func (m *MyStruct) MyMethod() error {
    // do something useful
}

または

func (self *MyStruct) MyMethod() error {
    // do something useful
}
39
miltonb

他の人の発言(特にPeterSOおよびdskinner-ピーターの回答への彼のコメント)に加えて、いくつかの重要な点に注意してください。

単純な関数のようにメソッドを呼び出すことができます

Goでは、anyメソッド関数を、レシーバーのメソッドとしてではなく、通常の関数として呼び出すことができます。定義されている型の名前で名前を修飾するだけです。メソッドになり、explicitlyレシーバー引数を渡す(メソッドから単純な関数を取得するには、 メソッド式 )。

実証するには:

_package main

import "fmt"

type Foo int

func (f Foo) Bar() {
    fmt.Printf("My receiver is %v\n", f)
}

func main() {
    a := Foo(46)
    a.Bar()
    b := Foo(51)
    Foo.Bar(b)
}
_

Playground link.

このプログラムを実行すると、次の内容が出力されます。

_My receiver is 46
My receiver is 51
_

ご覧のとおり、ここでselfはその神聖な意味を失います。これは、メソッドartificallyを呼び出して、それとは関係のないコンテキストを作成したためですよく引用されている「オブジェクトのメソッドを呼び出すことは、そのオブジェクトにメッセージを渡すこと」という概念です。

要約すると、Goでは、メソッドは、どのように呼び出されても、1つの追加の引数(そのレシーバー)を受け取る特定の型に意味的にバインドされた関数にすぎません。他の多くの主流言語とは異なり、Goはこの事実をカーペットの下に隠しません。

レシーバーは、そのタイプで定義されたメソッド内にmutableである必要はありません

私の例で示したように、ポインター以外のレシーバーにメソッドBar()を定義しました。レシーバーに値を割り当てようとすると、成功しても影響はありません。 Goのすべての要素と同様に、レシーバーが値で渡されたため(呼び出し元は整数がコピーされただけです)。

メソッドでレシーバーの値を変更できるようにするには、次のように適切に型指定されたポインターで定義する必要があります

_func (f *Foo) Bar() {
    // here you can mutate the value via *f, like
    *f = 73
}
_

繰り返しますが、selfを使用して「私」、「私の内部」を意味することは、ここでは意味がないことがわかります。私の例では、メソッドは、知っている型の値を受け取っただけです。これは、オブジェクトが通常参照によって渡されるブラックボックスである多くのOO言語とは対照的です。 Goでは、事実上すべてのメソッドを定義できます( 他のメソッドを含む 、これは_net/http_標準パッケージで使用されます)。オブジェクト」の概念。

メソッドの異なるセットは、異なる時点で同じ値に適用できる場合があります

Goでは、メソッドは特定のタイプを中心に機能をグループ化する便利な方法であり、プログラムフローのさまざまなポイントで同じ値に異なるメソッドのセットを適用できる場合があります。インターフェースとそれらが提供するダックタイピングを組み合わせると、このコンセプトは本当に繁栄します。 Goには、他の型の値に対して特定の操作を実行する「サポート」型を定義する慣用句があるという考え方です。

この良い例は、 標準パッケージsort です。たとえば、整数のスライスを並べ替えることができるIntSlice型を提供します—タイプ_[]int_の値。これを行うには、スライスを_sort.IntSlice_に型変換し、結果として得られる値には、スライスを並べ替えるためのメソッドのセット全体が含まれますが、値の内部表現には変更なし— _sort.IntSlice_は type IntSlice []int_ として定義されているためです。そのIntSliceタイプの各メソッドでは、レシーバー値の意味をselfと一致させるのは困難です。これは、単にタイプが他のタイプのメソッドのセットを提供するために存在するためです。哲学的な意味では、そのようなユーティリティタイプには「自己」の概念はありません;-)

結論

ですから、頭の中で物事をシンプルに保ち、Goがセマンティクスで採用した明確でシンプルなアプローチを "過負荷"にしないでくださいexplicitly提供します。

もう1つ注意してください。私がGoのイディオムを学んだときの私の個人的な認識は、Goの最も重要な特性が(理想主義などとは対照的に)その実用性であるということです。したがって、不自然に感じるような概念を見つけたら、なぜそれはそのように設計されており、ほとんどの場合、その概念がなぜ脳に「クリック」して自然になるのかを発見します。 (Goのメソッドを理解する上でこの特定の問題を理解するには、Cに精通していることは非常に役立ちます。)

44
kostix

this/self規約を回避する特に説得力のある理由はありません。ここでの他の投稿は、コミュニティの規範を引用するか、命名規則に関係のないメソッドディスパッチの側面を説明するだけです。

これらのコードレビューガイドライン 何も理由を付けずにthisまたはselfを拒否します。ただし、暗黙の主張に何か他の方法よりも重点が置かれていないことが読み取れない場合を除きます。言語。

thisまたはselfの規約を遵守することの1つの利点は、 デメテルの法則 の違反を強調表示するのに役立つことです。このようなコード:

func (w *ChunkWriter) Write(recId uint32, msg []byte) (recs uint64, err error) {
    recs = w.chunk.Records
    err = w.handle.Write(recId, msg)
    if err == nil {
        recs++
        w.chunk.Records = recs
    }
    return
}

その顔にwのメンバーに不適切に手を差し伸べているように見えます。実際、それはそのレシーバーのメンバーにアクセスしていますが、これは完全に適切です。

16
Oliver Goodman

Goウィキでは、thisselfのような用語を使用せず、代わりにタイプ名から派生した省略形を使用することをお勧めしています: https://github.com/golang/go/wiki/CodeReviewComments #receiver-names

14
dethtron5000

私はオリバー・グッドマンの答えに同意し、そのような圧倒的なオッズに直面した彼の信念の勇気を賞賛します。

基本的に、反対者は、レシーバ変数として「this」(私の好み)を使用しても、他の言語とまったく同じ意味はないと述べています。したがって、潜在的な問題を回避し、混乱を最小限に抑えるために、これを回避する必要があります。

しかし、もちろん、同じことが->.についても言えます。言うまでもなく、Goの他の多くのC風のファセット(制御できない)については言うまでもありません。

ただし、OPが意味する方法で「this」を使用すると、コンテキストの理解が非常に容易になります。そして、私たちはGoでプログラミングしていることを知っているので、他の言語と比較して異なる影響があることを理解しています。

「this」の使用は、タイプ(「クラス」)メソッドが「従来の」メソッドであることも意味します。奇妙さが予想される場合は、常に別のレシーバーモニカを使用してこの事実を強調できます。

しかし、お風呂で赤ちゃんを捨てないでください!

当初から、ハードコアのGopherは非常に硬直で曲がっていない傾向があることを発見しました。しかし、私はこれが多くの多くの分野でこの言語に役立ったことを認めなければなりません。その点で私は彼らに帽子を脱ぎました。

ただし、この同じ譲歩しない考え方は、他の分野でも問題を解き放ちます。GOPATH狂気は最も明白な例です。

専門家の意見を聞くことは、物事をじっくり考えないことの言い訳にはなりません。専門家でさえ、しばしばその専門性に正比例する大きさの盲点とハングアップがあります。

短い答え:自分に最も適した規則を使用してください!

6
RVJ Callanan

Goコマンドのソースコードと、優れたプログラミングスタイルの手本となるGo標準パッケージをご覧ください。名前selfは、Goメソッドのレシーバーには使用されません。

Goソースコード

名前selfはPython規約です。GoはPythonではありません。

Pythonチュートリアル

9.4。ランダムな発言

多くの場合、メソッドの最初の引数はselfと呼ばれます。これは単なる慣例にすぎません。セルフという名前は、Pythonにとってまったく特別な意味を持ちません。ただし、この規則に従わないと、コードが他のPythonプログラマーから読みにくくなる可能性があります。また、このような規則に依存するクラスブラウザープログラムが作成されることも考えられます。

3
peterSO