web-dev-qa-db-fra.com

Les canaux sont-ils passés par référence implicitement

La visite guidée a cet exemple pour les chaînes: 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)
}

Le canal c est modifié dans la fonction somme et les changements persistent après la fin de la fonction. Évidemment, c a été transmis par référence, mais aucun pointeur vers c n'a été créé. Les canaux sont-ils implicitement transmis par référence dans go?

48
lhk

Techniquement, ils sont copiés, car lorsque vous utilisez make, vous allouez quelque chose sur le tas, c'est donc techniquement un pointeur dans les coulisses. Mais le type de pointeur n'est pas exposé, ils peuvent donc être considérés comme un type de référence.

MODIFIER : D'après la spécification:

La fonction intégrée make prend un type T, qui doit être un type de tranche, de carte ou de canal, éventuellement suivi d'une liste d'expressions spécifiques au type. Il renvoie une valeur de type T (pas * T). La mémoire est initialisée comme décrit dans la section sur les valeurs initiales.

Un canal doit être initialisé avant de pouvoir être utilisé. Make le fait, il peut donc être utilisé comme type de référence.

Cela signifie essentiellement que vous pouvez le passer dans une fonction et y écrire ou y lire. La règle générale est que si vous utilisez make, new ou &, vous pouvez le passer à une autre fonction sans copier les données sous-jacentes.

Ainsi, les types suivants sont des "références":

  • tranches
  • plans
  • canaux
  • pointeurs
  • les fonctions

Seuls les types de données (nombres, bools et structs, etc.) sont copiés lors du passage dans une fonction. Les chaînes sont spéciales, car elles sont immuables, mais ne sont pas transmises par valeur. Cela signifie que les éléments suivants ne fonctionneront pas comme prévu:

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

Tout dans Go est passé et attribué par valeur. Certains types intégrés, y compris les types de canal et les types de carte, se comportent comme des pointeurs opaques vers une structure interne cachée. Et il est possible de modifier cette structure interne par des opérations sur le canal ou la carte. Ils commencent par nil, ce qui est analogue au pointeur nil.

9
newacct

Vous pourriez dire oui, mais dire que "le canal c est modifié dans la fonction somme" n'est pas vraiment la bonne terminologie. Les envois et les réceptions de canaux ne sont pas vraiment considérés comme des modifications.

Notez que les tranches et les cartes se comportent d'une manière similaire, voir http://golang.org/doc/effective_go.html pour plus de détails.

"Passé par référence" implique également qu'une affectation pourrait être faite à c dans sum qui changerait sa valeur (par opposition à ses données sous-jacentes) en dehors de sum, ce qui n'est pas le cas.

1
cthom06

Les variables de canal sont des références, mais cela dépend de votre définition de "référence". Spécification de langue ne mentionne jamais les types de référence.

Aucun canal (variable) n'est "modifié" dans la fonction sum. L'envoi vers un canal change son état.

En d'autres termes, oui, le canal est implémenté comme un pointeur vers une structure de temps d'exécution. Notez que cela est strictement nécessaire pour la sémantique de référence.

EDIT: La phrase ci-dessus était censée se lire: "Notez que ce n'est pas strictement nécessaire pour la sémantique de référence.", C'est-à-dire. le mot "non" est devenu MIA. Désolé pour toute confusion éventuellement créée.

1
zzzz