2012-12-20 25 views
12

Se ho una struct come questoVai: sto creando troppi valori?

type myStruct struct { 
    mystring string 
    myint int 
} 

e se ho una funzione che restituisce una nuova myStruct come questo

func New() myStruct { 
    s := myStruct{} 

    s.mystring = "string" 
    s.myint = 1 

    return s 
} 

Perché io prima memorizzarlo nella "s" variabile prima di restituirlo , la mia funzione sta effettivamente realizzando 2 valori myStruct invece di uno?

E se sì, è quindi una procedura migliore per assicurarsi che non lo memorizzi prima nella variabile?

+0

non sono sicuro, ma mi ricordo che c'era una sintassi come 'func Nuovo() (s myStruct) {...} 'che farebbe l'allocazione del risultato per te. Potrebbe essere più veloce. Usi google go o gccgo? –

+0

Sto usando il comando go di Google. Sarei curioso di sapere se questa sintassi avrebbe lo stesso effetto o se in qualche modo evitasse il problema. –

+0

vedi la risposta accettata ... definendolo come 'func New() (s myStruct)' permetterà al compilatore di allocare la struct per te prima di entrare nel tuo corpo della funzione. dovrebbe funzionare allo stesso modo della risposta di jdi. ancora non puoi essere sicuro, perché le specifiche/implementazioni di go stanno cambiando in modo permanente. –

risposta

11

Il return dichiarazione restituirà una copia del valore dell'oggetto myStruct. Se si tratta di un oggetto piccolo, allora va bene.

Se si intende per il chiamante per essere in grado di modificare questo oggetto, e la struct avrà metodi che utilizzano un puntatore come ricevitore, allora ha più senso per restituire un puntatore al struct invece:

func New() *myStruct { 
    s := myStruct{} 

    s.mystring = "string" 
    s.myint = 1 

    return &s 
} 

È possibile vedere la copia succede quando si confronta l'indirizzo di memoria del valore vs tipi puntatore di ritorno: http://play.golang.org/p/sj6mivYSHg

package main 

import (
    "fmt" 
) 

type myStruct struct { 
    mystring string 
    myint int 
} 

func NewObj() myStruct { 
    s := myStruct{} 
    fmt.Printf("%p\n", &s) 

    return s 
} 

func NewPtr() *myStruct { 
    s := &myStruct{} 
    fmt.Printf("%p\n",s) 
    return s 
} 

func main() { 

    o := NewObj() 
    fmt.Printf("%p\n",&o) 

    p := NewPtr() 
    fmt.Printf("%p\n",p) 
} 


0xf8400235a0 // obj inside NewObj() 
0xf840023580 // obj returned to caller 
0xf840023640 // ptr inside of NewPtr() 
0xf840023640 // ptr returned to caller 
+0

Grazie, in questo caso particolare, voglio restituire un myStruct valore invece di un puntatore, ma sembra che tu abbia ragione che restituisca una copia, a meno che non dichiari il 's' nella firma della funzione, che sembra dare un comportamento diverso. –

+0

Ah sì, guardare l'indirizzo della memoria lo chiarisce completamente per me. Grazie mille! –

+0

Ha, ho imparato molto dalle risposte Python, ora sto imparando dalle tue risposte Go - Vorrei fare ancora +1 se potessi. – RocketDonkey

3

Non sono assolutamente un esperto Go (o anche un principiante :)), ma come menzionato @ max.haredoom, è possibile allocare le variabili nella firma della funzione stessa. In questo modo, si può anche omettere il s nella return:

package main 

import "fmt" 

type myStruct struct { 
    mystring string 
    myint int 
} 

func New() (s myStruct) { 
    s.mystring = "string" 
    s.myint = 1 

    return 
} 

func main() { 
    r := New() 
    fmt.Println(r) 
} 

// Outputs {string 1} 

Negli esempi che ho incontrato in Effective Go, sembra essere il modo più comune di fare le cose di questa natura, ma ancora una volta , Non sono assolutamente un'autorità in materia (e cercherò ulteriori informazioni sulla performance effettiva).

+0

Grazie. Penso di aver trovato che la mia versione originale stava facendo una copia, ma facendo lo stesso test usando la tua versione, sembra che la copia sia evitata. Ecco il test che ho usato, ma aggiornato con la tua soluzione. http: //play.golang.org/p/66mqsVFbs_ –

+0

Basta non andare troppo pazzo con i ritorni impliciti, nella maggior parte dei casi tendono ad essere una cattiva pratica – jozefg

+0

@jozefg Sì, buon punto :) – RocketDonkey

0

Penso di aver trovato la risposta usando il differimento.

Ho aggiornato la funzione in modo che sia presente una modifica posticipata al valore myStruct. Ciò significa che accadrà dopo il ritorno, ma prima che venga ricevuto dall'altra parte.

Quando si esegue questa operazione, la struttura ricevuta dal chiamante non mostra il valore aggiornato, pertanto sembra che restituisca effettivamente una copia.

func New() myStruct { 
    s := myStruct{} 

    defer func() { 
     s.mystring = "new value" // defer an update to the value 
    }() 

    s.mystring = "string" 
    s.myint = 1 

    return s 
} 

func main() { 

    b := New() 

    fmt.Println(b) // still shows the original value 
} 

http://play.golang.org/p/WWQi8HpDny

Problemi correlati