web-dev-qa-db-ja.com

Google Go言語ではどのタイプが変更可能で不変ですか?

Google Goで、文字列は不変であるということを読みました。他のタイプはどうですか?少し年をとったプログラマーとして、私は不変性の利点を知っていますが、私は危険に生きることを好みます。

どのタイプが変更可能または不変であるかを知ることは非常に役立ちます。


更新、私が最も心配しているのは、可変または不変のタイプに応じた実用的な問題です。 Javaの典型的な例のように、ループでStringを作成し、10,000回ループすると、10,000のStringが作成され、後でガベージコレクションされます。これは、実際に私が働いていた会社のプロジェクトの深刻な問題でした。

問題は、Goの不変性が同じ問題を引き起こす場合があるかどうかです。

Varの扱い方に影響します。 (またはそうだと思います)。


もう一度更新します。他の実用的な問題についても心配しています。何かが不変であることを知っているということは、並列であるコードを記述でき、オブジェクトの1つの参照を更新しても他の参照は更新されないことを意味します。しかし、時には危険なことをしたいのですが、可変性を求めています。

これらは可変性と不変性の結果であり、コードの記述方法に影響します。

31
Phil

心配する必要はありません-Goを使用すると、本当に自分の足で撃つことができます:-)

GoはErlangのようなものではありません。Erlangは、あなたが質問で得ているものかもしれません。

_x := 1
x = 2
_

_1_の値で1つの変数xを割り当て、それを_2_に再割り当てします-ここには追加のメモリは割り当てられません。

お気づきのように、文字列は不変なので、文字列操作を行うとコピーが作成される可能性があります。文字データをインプレース変更したい場合は、bytesパッケージを介して_[]byte_の変数を操作することをお勧めします。

これに関するRuss Coxの投稿は、基本的なデータ構造に関するほとんどの質問に答えるものです。 http://research.swtch.com/2009/11/go-data-structures.html

他のコメンターが述べたように、Go関数の値のセマンティクスを確認する必要があります-最初は少し意外かもしれません。

次の機能がある場合:

_func (t MyType) myFunc() {
    // do something to set a field in t
}
_

そしてあなたはあなたのコードを呼び出す

_myVar.myFunc()
_

myFunc()に表示されるtは実際にはcopyであるため、これが期待どおりの動作をしないことに驚くかもしれません。/myVar

ただし、次のは機能します

_func (t *myType) myFunc() {
    // do something to set a field in t
}
_

関数にはpointerのコピーがあり、そのポインターを介して基本構造にアクセスできるためです。

32
robothor

私の意見では、最初に次の2つの概念を分離する必要があります。

  • 数学的オブジェクトとしての整数(つまり:値)

  • タイプintの変数

それから答えは:整数変数は変更可能、整数値は不変です。

このビューは、文字列は不変であると述べているGo仕様と一致しています。明らかに、文字列variableは変更可能です。

Goの変数(概念として)は少なくとも次のとおりです。

  • 名前付き変数(例:var i int
  • ポインタを介してアクセス可能な変数

可変Goオブジェクト:

  • 配列とスライス
  • 地図
  • チャンネル
  • 外側のスコープから少なくとも1つの変数をキャプチャしているクロージャ

不変のGoオブジェクト:

  • インターフェース
  • ブール値、数値(タイプintの値を含む)
  • 文字列
  • ポインタ
  • 関数ポインター、および関数ポインターに縮小できるクロージャー
  • 単一のフィールドを持つ構造体

他の人々がそれらを不変であると考えるかもしれない間、何人かの人々が可変であると考えるかもしれないオブジェクトを動かしてください:

  • 複数のフィールドを持つ構造体
11
user811773

「可変性」は、「内部」部分を持ち、おそらくそれを含むものとは独立して変更できる、ある複合型について話す場合にのみ意味があります。文字列は自然に文字で構成されており、新しい文字列全体を割り当てる以外に、既存の文字列の文字を変更できるメカニズムは言語にはないため、不変であると言えます。

Intの場合、可変性について話すことは実際には意味がありません。なぜなら、intの「コンポーネント」は何ですか?まったく新しいintを割り当てることでintを変更しますが、割り当ては「変更」としてカウントされません。

可変性の問題と参照型と値型の間にいくつかの関連があります。意味的には、不変の参照型と値の型の間に違いはありません。どうして? intが実際には不変オブジェクトへのポインタであると仮定します(つまり、*InternalIntObjectを変更するための関数はありませんInternalIntObject)。このようなポインタを変数に割り当てると、オブジェクトは不変であるため、同じ整数値を永遠に表します(同じオブジェクトを共有する他のユーザーが変更することはできません)。これは整数値型と同じ動作です。代入演算子によってintを割り当てることができます。同様に、これらのポインターを割り当てによって割り当てることができます。結果は同じです。割り当てられた変数は、割り当てられた変数と同じ整数を表します。違いは比較のみで、結果を計算するためにポインターを逆参照するには算術演算子を再定義する必要があります。

したがって、可変性は参照型に対してのみ意味があります。

あなたが尋ねたものに関しては、「可変」タイプは通常、文字列を除く参照タイプであると見なされます:マップ、チャネル、スライス(スライスによってポイントされたデータに関して)、および何かへのポインター(変更できるため)ポインターが指す場所の値)。

2
newacct

はい、WordimmutableはGo仕様で1回だけ表示されます。そしてそれはtype stringAssignabilityAddressability の2つの観点からもっと見るべきだと思います。たとえば、Goでは、明らかに、エクスポートされていないプロパティを使用して、変数を型の異なる値に再バインドすることが禁止されます。コピーコンストラクタを提供しないクラスのC++のようなちょっとですが、Go Pimpl は、ゴルーチンにふさわしくなく、コミュニケーション哲学。

2
ypb

あなたの懸念は、不変性よりも割り当てにあるようです。不変性は、メモリの再利用を不可能にすることにより、割り当てに確実に影響します。賢いコンパイラーは、それが知っているアドレスがエスケープしない「不変の」メモリーを再利用する可能性があります。

文字列は別として、インターフェースには注意してください。 Wordのサイズよりも大きいものは、インターフェイスに割り当てるときに割り当てる必要があります(最適化は別として)。また、ループ本体で宣言された変数は、クロージャー経由を含め、アドレスがエスケープされるため、ループを介して毎回割り当てる必要があります。それ以外の場合は、割り当ては単なる割り当てです。値は、変数によって表されるメモリにコピーされます。

ループ内でmakeまたはnewを使用する場合、または参照を生成するリテラルを使用する場合は、割り当てを行う必要があります(これも最適化の対象となります)。

基本的に、すべては、できる限りメモリを再利用しようとすることであり、そうすることが理にかなっている場合は、コンパイラができないときにコンパイラが代わりにやってくれることを望んでいます。

1
SteveMcQwark