web-dev-qa-db-ja.com

チャンネルは暗黙的に参照渡しされますか

Goツアーには、チャンネル用の次の例があります。 https://tour.golang.org/concurrency/2

package main

import "fmt"

func sum(a []int, c chan int) {
    sum := 0
    for _, v := range a {
        sum += v
    }
    c <- sum // send sum to c
}

func main() {
    a := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(a[:len(a)/2], c)
    go sum(a[len(a)/2:], c)
    x, y := <-c, <-c // receive from c

    fmt.Println(x, y, x+y)
}

チャネルcはsum関数で変更され、関数が終了した後も変更は持続します。明らかにcは参照渡しされましたが、cへのポインタは作成されませんでした。 goの参照により、チャネルは暗黙的に渡されますか?

48
lhk

技術的にはコピーされます。makeを使用すると、ヒープに何かを割り当てているため、技術的には背後のポインターになります。しかし、ポインタ型は公開されていないため、参照型と考えることができます。

[〜#〜] edit [〜#〜]:仕様から:

組み込み関数makeは、タイプTを取ります。これは、スライス、マップ、またはチャネルタイプでなければならず、オプションでタイプ固有の式のリストが続きます。タイプT(* Tではない)の値を返します。メモリは、初期値のセクションで説明されているように初期化されます。

チャネルは、使用する前に初期化する必要があります。 Makeはこれを行うため、参照型として使用できます。

これが基本的に意味することは、それを関数に渡して、そこから読み書きできることです。一般的な経験則は、makenew、または&、基になるデータをコピーせずに別の関数に渡すことができます。

したがって、以下は「参照」タイプです。

  • スライス
  • 地図
  • チャンネル
  • ポインタ
  • 関数

関数に渡すときは、データ型(数値、ブール、構造体など)のみがコピーされます。文字列は不変ですが、値渡しではないため、特別です。これは、以下が期待どおりに機能しないことを意味します。

type A struct {
    b int
}
func f(a A) {
    a.b = 3
}
func main() {
    s := A{}
    f(s)
    println(s.b) // prints 0
}
57
beatgammit

Goのすべては、値によって渡され、割り当てられます。チャネルタイプやマップタイプなどの特定の組み込みタイプは、非表示の内部構造への不透明なポインターとして動作します。また、チャネルまたはマップに対する操作により、その内部構造を変更することができます。これらは、nilポインターに似たnilから始まります。

9
newacct

「はい」と言うこともできますが、「チャネルcは合計関数で変更されます」と言うのは、実際には正しい用語ではありません。チャネルの送受信は、実際には変更とは見なされません。

スライスとマップは同じように動作することに注意してください。詳細については http://golang.org/doc/effective_go.html をご覧ください。

また、「参照渡し」は、c内のsumに割り当てを行って、sum以外の値(基になるデータではなく)を変更できることを意味しますが、そうではありません。

1
cthom06

チャネル変数は参照ですが、「参照」の定義に依存します。 言語仕様 参照型に言及することはありません。

sum関数で「変更」されるチャネル(変数)はありません。チャンネルに送信すると、状態が変わります。

言い換えれば、はい、チャネルは実行時構造へのポインタとして実装されます。参照セマンティクスには厳密に必要なことに注意してください。

編集:上記の文は、「参照セマンティクスに厳密に必要なnotであることに注意してください。」、つまり「ない」という言葉はMIAに行きました。最終的に作成された混乱のためにすみません。

1
zzzz