Digitare la conversione di sezioni di interfacce


194

Sono curioso di sapere il motivo per cui Go does't implicitamente convertito []Ta []interface{}quando sarà convertire implicitamente Ta interface{}. C'è qualcosa di non banale in questa conversione che mi manca?

Esempio:

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

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

go build si lamenta

non è possibile utilizzare una stringa (tipo []) come tipo [] interfaccia {} nell'argomento della funzione

E se provo a farlo esplicitamente, stessa cosa: si b := []interface{}(a)lamenta

impossibile convertire una stringa (tipo []) in tipo [] interfaccia {}

Quindi ogni volta che devo fare questa conversione (che sembra venire molto), ho fatto qualcosa del genere:

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

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


quindi definisci "converti implicitamente [] T in [] interfaccia {}" per indicare "allocare una nuova porzione e copiare tutti gli elementi". Ciò è incompatibile con ciò che fa "convertire implicitamente T in interfaccia {}", che è solo una vista dello stesso valore di un tipo statico più generico; nulla viene copiato; e se lo affermi nuovamente al tipo T, otterrai comunque la stessa cosa.
newacct

1
non pensavo troppo dettagliatamente a come sarebbe stato implementato, solo che a un livello superiore sarebbe conveniente poter convertire facilmente un intero elenco in un altro tipo come soluzione alternativa per non avere tipi generici.
Danny,

Risposte:


215

In Go, esiste una regola generale secondo cui la sintassi non deve nascondere operazioni complesse / costose. La conversione di a stringin interface{}viene eseguita in O (1) tempo. La conversione di a []stringin interface{}viene eseguita anche in O (1) poiché una sezione ha ancora un valore. Tuttavia, la conversione di a []stringin []interface{}è tempo O (n) perché ogni elemento della sezione deve essere convertito in an interface{}.

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

Non esiste una funzione di libreria standard che eseguirà questa conversione per te. Potresti crearne uno con reflection, ma sarebbe più lento dell'opzione a tre righe.

Esempio con 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 tua migliore opzione però è solo usare le righe di codice che hai fornito nella tua domanda:

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

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

Tutta questa risposta vale anche per maps.
Ricky:

Questo vale anche per channels.
Justin Ohms,

Grazie per una chiara spiegazione, se possibile è possibile aggiungere rif. per Converting a []string to an interface{} is also done in O(1) time?
Mayur,

56

Quello che ti manca è quello Te interface{}che ha un valore di Tavere diverse rappresentazioni in memoria, quindi non può essere banalmente convertito.

Una variabile di tipo Tè solo il suo valore in memoria. Non ci sono informazioni sul tipo associato (in Go ogni variabile ha un solo tipo noto in fase di compilazione e non in fase di esecuzione). È rappresentato in memoria in questo modo:

  • valore

Una interface{}holding con una variabile di tipo Tè rappresentata in memoria in questo modo

  • puntatore per digitare T
  • valore

Quindi, tornando alla tua domanda originale: perché andare non si converte implicitamente []Tin []interface{}?

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


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

13

Ecco la spiegazione ufficiale: https://github.com/golang/go/wiki/InterfaceSlice

var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
    interfaceSlice[i] = d
}

Buone informazioni ma non è una spiegazione ufficiale. È una wiki in cui tutti possono modificare.
Inanc Gumus,

Come devo convertire []interfacenel tipo che mi aspetto come []map[string]interface{}?
Lewis Chan,

8

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

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

3
Ciò porta alla domanda successiva: come si suppone quindi che l'OP possa iterare barper interpretarlo come "una porzione di qualsiasi tipo"? Nota che il suo tre-liner crea una []interface{}, non []stringo una fetta di altro tipo concreto.
kostix,

14
-1: Funziona solo se la barra è [] stringa, nel qual caso puoi anche scrivere:func foo(bar []string) { /* ... */ }
weberc2

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

OP dovrà comunque capire il suo tipo in fase di esecuzione. L'uso di non slice interface{}farà sì che il compilatore non si lamenti quando si passa un argomento come in foo(a). Poteva usare il riflesso o il cambio di tipo ( golang.org/doc/effective_go.html#type_switch ) all'interno foo.
Cassiohg,

2

Converti interface{}in qualsiasi tipo.

Sintassi:

result := interface.(datatype)

Esempio:

var employee interface{} = []string{"Jhon", "Arya"}
result := employee.([]string)   //result type is []string.

2
Ne sei sicuro? Non penso sia valido. Lo vedrai: invalid type assertion: potato.([]string) (non-interface type []interface {} on left)
Adriano Tadao il

2

Nel caso abbiate bisogno di più cortocircuito nel codice, è possibile creare un nuovo tipo per l'helper

type Strings []string

func (ss Strings) ToInterfaceSlice() []interface{} {
    iface := make([]interface{}, len(ss))
    for i := range ss {
        iface[i] = ss[i]
    }
    return iface
}

poi

a := []strings{"a", "b", "c", "d"}
sliceIFace := Strings(a).ToInterfaceSlice()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.