2

次の例を考えてみましょう。「バックグラウンドで」何が起こっているのか完全には理解しておらず、説明を求めています。このバージョンでは、main関数からFoo呼び出すと構造体のコピーが作成されるようです。AddToEntry右?コードでこれを「証明」するにはどうすればよいですか?

goが構造体のコピーを作成するとき、私は構造体のコピーを操作しているだけで、main関数に戻ると、以前と同じように元のファイルが表示されますか?

ポインタを期待する場合(コードのコメントを参照)、すべてが正常であり、構造体はコピーされません。この種の「エラー」をどのように回避できますか?構造体をコピーしていないことを確認するにはどうすればよいですか?そのためのコンパイル時/実行時チェックの可能性はありますか、それとも注意が必要ですか?

package main

import (
    "fmt"
)

type Foo struct {
    Entry []string
}

func MakeFoo() Foo {
    a:=Foo{}
    a.Entry = append(a.Entry,"first")
    return a
}

// if I change (f Foo) to (f *Foo), I get 
// the "desired" result
func (f Foo) AddToEntry() {
    f.Entry = append(f.Entry,"second")
}


func main() {
    f:=MakeFoo()
    fmt.Println(f) // {[first]}
    f.AddToEntry()
    fmt.Println(f) // {[first]}
}
4

3 に答える 3

8

メソッドのシグネチャはfunc (f Foo) AddToEntry()です。メソッドの動作方法f.AddToEntry()は、次と同じです。

g := Foo.AddToEntry
g(f)

レシーバーは単なる別のパラメーターです。何でこれが大切ですか?構造体を渡して関数で変更するとどうなりますか?C、Go、およびその他の値渡し言語では、パラメーターで指定された構造体は単なるコピーです。したがって、オリジナルを変更することはできません。新しい構造体のみを返します。

を定義するときfunc (f *Foo) AddToEntry()は、最初のパラメーターであるレシーバーをポインターとして定義します。明らかに、ポインタがあれば、元の構造体を変更できます。隠されているのは、Goで構造体にアクセスするときに暗黙的に参照していることです。別の言い方をすれば、囲碁(*ptrFoo).Entryと同じです。ptrFoo.Entry

したがって、ここでの問題は、慣れていない人にとって、構文が何が起こっているのかを隠しているということです。Cでは、構造体へのポインタを渡さない限り、構造体を編集することはできません。Goでも同じことが起こります。受信しているものを変更するには、ポインターレシーバーを使用する必要があります。

于 2012-08-05T19:25:20.357 に答える
5

このGoのドキュメントを読んだことがありますか?

値またはポインターにメソッドを定義する必要がありますか?

方法:ポインターと値

Goプログラミング言語仕様

于 2012-08-05T19:03:25.363 に答える
2

構造体をコピーしていないことを確認するにはどうすればよいですか?そのためのコンパイル時/実行時チェックの可能性はありますか、それとも注意が必要ですか?

ここでの簡単な答えは、いいえ、これについてコンパイル時または実行時(1)のチェックを行うことはできないということです-注意する必要があります。goに少し慣れれば、これは自然なことです。

(1)技術的には、関数は型スイッチ を使用して型がポインターであるかどうかを照会できますが、それを覚えている場合は、パラメーターをポインターにすることも忘れないでください。

于 2012-08-05T19:42:12.090 に答える