2012-10-05 5 views
98

Sono curioso perché andare does't convertire implicitamente []T-[]interface{} quando sarà convertire implicitamente T a interface{}. C'è qualcosa di non banale in questa conversione che mi manca?Tipo conversione fette di interfacce a go

Esempio:

func foo([]interface{}) { /* do something */ } 

func main() { 
    var a []string = []string{"hello", "world"} 
    foo(a) 
} 

go build lamenta

non può utilizzare un (tipo stringa []) come tipo di [] interfaccia {} nella funzione argomento

E se cerco per farlo in modo esplicito, stessa cosa: b := []interface{}(a) lamentele

non può convertire un (tipo string []) per digitare [] interfaccia {}

Così ogni volta che ho bisogno di fare questa conversione (che sembra venire un sacco), ho fatto qualcosa di simile:

b = make([]interface{}, len(a), len(a)) 
for i := range a { 
    b[i] = a[i] 
} 

C'è un modo migliore per farlo, o le funzioni di libreria standard per aiutare con queste conversioni? Sembra un po 'sciocco scrivere 4 linee in più di codice ogni volta che voglio chiamare una funzione che può prendere un elenco di ad es. intarsi o stringhe.

+0

così si definisce "convertire implicitamente [] T su [] interfaccia {}" per significare "l'assegnazione di una nuova fetta e la copia di tutti gli elementi sopra". Ciò è incoerente con ciò che "converte implicitamente T in interface {}", che è solo una vista dello stesso valore di un tipo statico più generico; nulla viene copiato; e se digiti di nuovo il comando per digitare T, otterrai comunque la stessa cosa. – newacct

+0

non stava pensando troppo a come sarebbe implementato, solo che a un livello più alto sarebbe opportuno essere in grado di convertire facilmente un intero elenco in un altro tipo come soluzione alternativa per non avere tipi generici. – danny

+0

Buona domanda. :) – weberc2

risposta

104

In Go, c'è una regola generale che la sintassi non dovrebbe nascondere operazioni complesse/costose. La conversione di un string in un interface{} avviene in tempo O (1). La conversione di un []string in un interface{} viene eseguita anche in tempo O (1) poiché una porzione ha ancora un valore. Tuttavia, la conversione di un []string in un []interface{} è O (n), poiché ogni elemento della sezione deve essere convertito in un interface{}.

L'unica eccezione a questa regola è la conversione di stringhe. Quando si converte un string in e da un []byte o un []rune, Go fa O (n) funziona anche se le conversioni sono "sintassi".

Non esiste alcuna funzione di libreria standard che eseguirà questa conversione. Potresti farne uno con reflect, ma sarebbe più lento dell'opzione a tre linee.

Esempio con la riflessione:

func InterfaceSlice(slice interface{}) []interface{} { 
    s := reflect.ValueOf(slice) 
    if s.Kind() != reflect.Slice { 
     panic("InterfaceSlice() given a non-slice type") 
    } 

    ret := make([]interface{}, s.Len()) 

    for i:=0; i<s.Len(); i++ { 
     ret[i] = s.Index(i).Interface() 
    } 

    return ret 
} 

La soluzione migliore anche se è solo quello di utilizzare le linee di codice hai dato nella tua domanda:

b := make([]interface{}, len(a)) 
for i := range a { 
    b[i] = a[i] 
} 
+2

potrebbe essere più lento, ma funzionerebbe genericamente con qualsiasi tipo di slice – newacct

+0

Questa intera risposta si applica a 'map's too btw. Interfaccia – RickyA

+0

, non intefaccia. Che trucco :) – Jacob

3

Prova interface{} invece. Per eseguire il cast come slice, prova

func foo(bar interface{}) { 
    s := bar.([]string) 
    // ... 
} 
+2

Questo fa sorgere la domanda successiva: come si suppone che l'OP esegua l'iterazione su 'bar' in modo da interpretarlo come" una porzione di qualsiasi tipo "? Nota che il suo three-liner crea un'interfaccia '[] {}', non '[] stringa' o una porzione di altro tipo concreto. – kostix

+5

-1: Funziona solo se la barra è [] stringa, nel qual caso, si può anche scrivere: 'func foo (bar [] stringa) {/ * ... * /}' – weberc2

+1

usa un interruttore di tipo, o usa la riflessione come nella risposta accettata. – dskinner

33

La cosa si sta perdendo è che T e interface{} che contiene un valore di T ha diverse rappresentazioni in memoria quindi non può essere trasformato banalmente.

Una variabile di tipo T è solo il suo valore in memoria.Non ci sono informazioni sul tipo associato (in Vai ogni variabile ha un singolo tipo noto al momento della compilazione non in fase di esecuzione). Esso è rappresentato in memoria come questo:

  • valore

Un interface{} possesso di una variabile di tipo T è rappresentato in memoria come questo puntatore

  • digitare T
  • valore

Tornando alla tua domanda iniziale: perché go non converte implicitamente []T in []interface{}?

La conversione da []T a []interface{} implica la creazione di una nuova porzione di valori interface {} che è un'operazione non banale poiché il layout in memoria è completamente diverso.

+2

Grazie per aver effettivamente spiegato l'essenza del problema – kostix

+4

Questo è informativo e ben scritto (+1), ma non stai affrontando l'altra parte della sua domanda: "C'è un modo migliore per farlo ..." (- 1). – weberc2