2013-05-16 13 views
29

Il tour Go ha questo esempio per i canali: http://tour.golang.org/#63sono canali passati per riferimento implicitamente

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) 
} 

Il canale c viene modificata nella funzione somma ei cambiamenti persistere anche dopo la funzione è terminata. Ovviamente c è stato passato per riferimento ma non è stato creato alcun puntatore a c. I canali passano implicitamente per riferimento in go?

+0

Sì, i tipi di riferimento in Go sono 'slice',' map' e 'channel'. Passando questi, stai facendo una copia del riferimento. * (Anche le stringhe sono implementate come un tipo di riferimento, sebbene siano immutabili). * –

risposta

41

Tecnicamente vengono copiati, perché quando si utilizza make, si assegna qualcosa sullo heap, quindi è tecnicamente un puntatore dietro le quinte. Ma il tipo di puntatore non è esposto, quindi possono essere pensati come un tipo di riferimento.

EDIT: Dal spec:

La funzione make incorporata prende un tipo T, che deve essere una fetta, mappa o tipo di canale, eventualmente seguita da un elenco di tipo specifico espressioni. Restituisce un valore di tipo T (non * T). La memoria viene inizializzata come descritto nella sezione sui valori iniziali.

Un canale deve essere inizializzato prima di poter essere utilizzato. Make fa questo, quindi può essere usato come un tipo di riferimento.

Ciò significa in pratica che è possibile passarlo in una funzione e scrivere o leggere da esso. La regola generale è se si utilizza make, new o &, è possibile passarlo a un'altra funzione senza copiare i dati sottostanti.

Così, i seguenti sono tipi "di riferimento":

  • fette
  • alle mappe
  • canali
  • puntatori
  • funzioni

solo i tipi di dati (numeri, Caccio e structs, etc) vengono copiati quando si passa a una funzione. Le stringhe sono speciali, perché sono immutabili, ma non passate per valore. Ciò significa che quanto segue non funzionerà come previsto:

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

@tjameson: make non implica l'allocazione dell'heap e la slice viene effettivamente implementata come una struct e copiata quando viene passata. – zzzz

+0

@squint - Giusto. Con 'array', intendevo' make ([] int, 5) ', ma ho appena realizzato che questa è tecnicamente una fetta. Colpa mia. – tjameson

+0

@jnml - Esaminando le specifiche, immagino che tecnicamente non sia il caso. Io modifico – tjameson

1

Si potrebbe dire si, ma per dire "il canale c è modificato nella funzione somma" non è proprio la terminologia corretta. Il canale invia e riceve non sono realmente considerati modifiche.

Si noti che le sezioni e le mappe si comportano in modo simile, vedere http://golang.org/doc/effective_go.html per ulteriori dettagli.

Anche "passato per riferimento" implica che è possibile eseguire un'assegnazione a c in sum che cambierebbe il suo valore (al contrario dei suoi dati sottostanti) al di fuori della somma, il che non è il caso.

1

Le variabili di canale sono riferimenti, ma dipende dalla definizione di "riferimento". Language specification non menziona mai i tipi di riferimento.

Nessun canale (variabile) è 'modificato' nella funzione sum. L'invio a un canale cambia il suo stato.

In altre parole, sì il canale è implementato come puntatore a una struttura di tempo di esecuzione. Nota che è strettamente necessario per la semantica di riferimento.

MODIFICA: la frase precedente intendeva leggere: "Si noti che questo è non strettamente necessario per la semantica di riferimento.", Vale a dire. la parola "non" è andata a MIA. Ci scusiamo per qualsiasi confusione creata alla fine.

5

Tutto in Go viene passato e assegnato in base al valore. Alcuni tipi built-in, inclusi i tipi di canali e i tipi di mappe, si comportano come puntatori opachi a una struttura interna nascosta. Ed è possibile modificare quella struttura interna mediante operazioni sul canale o sulla mappa. Iniziano come nil, che è analogo al puntatore nil.

Problemi correlati