Impossibile convertire [] stringa in [] interfaccia {}


96

Sto scrivendo del codice e ne ho bisogno per catturare gli argomenti e passarli attraverso fmt.Println
(voglio il suo comportamento predefinito, scrivere argomenti separati da spazi e seguiti da una nuova riga). Tuttavia ci vuole []interface {}ma flag.Args()restituisce un file []string.
Ecco l'esempio di codice:

package main

import (
    "fmt"
    "flag"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Args()...)
}

Ciò restituisce il seguente errore:

./example.go:10: cannot use args (type []string) as type []interface {} in function argument

è un insetto? Non dovrebbe fmt.Printlnprendere alcun array? A proposito, ho anche provato a fare questo:

var args = []interface{}(flag.Args())

ma ottengo il seguente errore:

cannot convert flag.Args() (type []string) to type []interface {}

C'è un modo "Go" per risolvere questo problema?


1
Stavo scherzando con un semplice esempio ( go run test.go some test flags), e sembrava funzionare quando si cambia flags.Args()...in solo flag.Args()(l'output è [some test flags], seguito dalla nuova riga; sembrava funzionare anche con la registrazione dei flag effettivi). Non farò finta di capire perché, e la risposta di Stephen è comunque molto più istruttiva :)
RocketDonkey

Risposte:


116

Questo non è un bug. fmt.Println()richiede un []interface{}tipo. Ciò significa che deve essere una porzione di interface{}valori e non "una porzione qualsiasi". Per convertire la fetta, dovrai eseguire il loop e copiare ogni elemento.

old := flag.Args()
new := make([]interface{}, len(old))
for i, v := range old {
    new[i] = v
}
fmt.Println(new...)

Il motivo per cui non è possibile utilizzare alcuna slice è che la conversione tra a []stringe a []interface{}richiede la modifica del layout di memoria e avviene in tempo O (n). La conversione di un tipo in un interface{}richiede tempo O (1). Se rendessero non necessario questo ciclo for, il compilatore avrebbe comunque bisogno di inserirlo.


2
A proposito, ho trovato questo link in golang-nuts: groups.google.com/d/topic/golang-nuts/Il-tO1xtAyE/discussion
cruizh

2
Sì, ogni iterazione richiede tempo O (1) e il ciclo richiede tempo O (n). Questo è quello che ho detto. Per quanto riguarda la funzione che riceve a []string, si aspetta a interface{}. An interface{}ha un layout di memoria diverso da a, stringquindi il problema è che ogni elemento deve essere convertito.
Stephen Weinberg

1
@karlrh: No, supponiamo che la Printlnfunzione modifichi la sezione e imposti alcuni elementi (non lo fa, ma supponiamo che lo faccia). Quindi può metterne qualsiasi interface{}nella fetta, che dovrebbe avere solo strings. Quello che vuoi veramente è qualcosa come il carattere jolly Java Generics Slice<? extends []interface{}>, ma non esiste in Go.
newacct

4
Append è magico. È integrato e trattato in modo speciale dalla lingua. Altri esempi includono new(), len()e copy(). golang.org/ref/spec#Appending_and_copying_slices
Stephen Weinberg

1
Oggi ho imparato che "nuovo" non è una parola chiave di riserva! Né è fare!
Sridhar

11

Se si desidera stampare solo una porzione di stringhe, è possibile evitare la conversione e ottenere lo stesso identico output unendo:

package main

import (
    "fmt"
    "flag"
    "strings"
)

func main() {
    flag.Parse()
    s := strings.Join(flag.Args(), " ")
    fmt.Println(s)
}

11

In questo caso, non è necessaria una conversione del tipo. Basta passare il flag.Args()valore a fmt.Println.


Domanda:

Impossibile convertire [] stringa in [] interfaccia {}

Sto scrivendo del codice e ne ho bisogno per catturare gli argomenti e passarli attraverso fmt.Println (voglio il suo comportamento predefinito, scrivere argomenti separati da spazi e seguiti da una nuova riga).

Ecco l'esempio di codice:

package main

import (
    "fmt"
    "flag"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Args()...)
}

Flag del pacchetto

import "flag"

func Args

func Args() []string

Args restituisce gli argomenti della riga di comando non flag.


Pacchetto fmt

import "fmt"

func Println

func Println(a ...interface{}) (n int, err error)

Printlnformati utilizzando i formati predefiniti per i suoi operandi e scrive sullo standard output. Gli spazi vengono sempre aggiunti tra gli operandi e viene aggiunta una nuova riga. Restituisce il numero di byte scritti e qualsiasi errore di scrittura riscontrato.


In questo caso, non è necessaria una conversione del tipo. Basta passare il flag.Args()valore a fmt.Println, che utilizza la riflessione per interpretare il valore come tipo []string. Il pacchetto reflectimplementa la riflessione in fase di esecuzione, consentendo a un programma di manipolare oggetti con tipi arbitrari. Per esempio,

args.go:

package main

import (
    "flag"
    "fmt"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Args())
}

Produzione:

$ go build args.go
$ ./args arg0 arg1
[arg0 arg1]
$ 

Se volessi omettere le parentesi, la conversione non sarebbe comunque necessaria?
cruizh

1

In Go, una funzione può accettare solo argomenti dei tipi specificati nell'elenco dei parametri nella definizione della funzione. La caratteristica del linguaggio dei parametri variadici complica un po 'la cosa, ma segue regole ben definite.

La firma della funzione per fmt.Printlnè:

func Println(a ...interface{}) (n int, err error)

Secondo la lingua specifica ,

Il parametro finale in entrata in una firma di funzione può avere un tipo con prefisso .... Una funzione con un tale parametro è chiamata variadic e può essere invocata con zero o più argomenti per quel parametro.

Ciò significa che puoi passare Printlnun elenco di argomenti di interface{}tipo. Poiché tutti i tipi implementano l'interfaccia vuota, puoi passare un elenco di argomenti di qualsiasi tipo, che è il modo in cui puoi chiamare Println(1, "one", true), ad esempio, senza errori. Consulta la sezione "Passaggio di argomenti a ... parametri" della specifica del linguaggio:

il valore passato è una nuova slice di tipo [] T con un nuovo array sottostante i cui elementi successivi sono gli argomenti effettivi, che devono essere tutti assegnabili a T.

La parte che ti dà problemi è subito dopo nelle specifiche:

Se l'argomento finale è assegnabile a un tipo di slice [] T, può essere passato invariato come valore per un parametro ... T se l'argomento è seguito da .... In questo caso non viene creata una nuova slice.

flag.Args()è il tipo []string. Dal momento Tin Printlnè interface{}, []Tè []interface{}. Quindi la domanda si riduce a se un valore di slice di stringa è assegnabile a una variabile di tipo slice di interfaccia. Puoi facilmente testarlo nel tuo codice go tentando un compito, ad esempio:

s := []string{}
var i []interface{}
i = s

Se si tenta una tale assegnazione, il compilatore emetterà questo messaggio di errore:

cannot use s (type []string) as type []interface {} in assignment

Ed è per questo che non puoi usare i puntini di sospensione dopo una sezione di stringa come argomento per fmt.Println . Non è un bug, funziona come previsto.

Ci sono un sacco di modi ancora è possibile stampare flag.Args()con Println, ad esempio

fmt.Println(flag.Args())

(che verrà restituito come [elem0 elem1 ...]da documentazione del pacchetto fmt )

o

fmt.Println(strings.Join(flag.Args(), ` `)

(che produrrà gli elementi slice di stringa, ciascuno separato da un singolo spazio) utilizzando la funzione Join nel pacchetto di stringhe con un separatore di stringa, ad esempio.


0

Penso che sia possibile usare la riflessione, ma non so se sia una buona soluzione

package main

import (
    "fmt"
    "reflect"
    "strings"
)

type User struct {
    Name string
    Age  byte
}

func main() {
    flag.Parse()
    fmt.Println(String(flag.Args()))
    fmt.Println(String([]string{"hello", "world"}))
    fmt.Println(String([]int{1, 2, 3, 4, 5, 6}))
    u1, u2 := User{Name: "John", Age: 30},
        User{Name: "Not John", Age: 20}
    fmt.Println(String([]User{u1, u2}))
}

func String(v interface{}) string {
    val := reflect.ValueOf(v)
    if val.Kind() == reflect.Array || val.Kind() == reflect.Slice {
        l := val.Len()
        if l == 0 {
            return ""
        }
        if l == 1 {
            return fmt.Sprint(val.Index(0))
        }
        sb := strings.Builder{}
        sb.Grow(l * 4)
        sb.WriteString(fmt.Sprint(val.Index(0)))
        for i := 1; i < l; i++ {
            sb.WriteString(",")
            sb.WriteString(fmt.Sprint(val.Index(i)))
        }
        return sb.String()
    }

    return fmt.Sprintln(v)
}

Produzione:

$ go run .\main.go arg1 arg2
arg1,arg2
hello,world
1,2,3,4,5,6
{John 30},{Not John 20}

-1

fmt.Printlnaccetta un parametro variadico

func Println (a ... interface {}) (n int, err error)

È possibile stampare flag.Args()senza convertire in[]interface{}

func main() {
    flag.Parse()
    fmt.Println(flag.Args())
}

2
fmt.PrintlnLa firma di non è cambiata in oltre 6 anni (e questo era solo un cambio di pacchetto per error). Anche se lo avesse fatto, la specifica dice chiaramente "variadico con un parametro finale p di tipo ... T, quindi entro f il tipo di p è equivalente al tipo [] T" quindi non avrebbe importanza. fmt.Println(flags.Args()...)ancora non funziona (ti manca l'espansione slice), ha fmt.Println(flags.Args())sempre funzionato.
Marc

Come hai ottenuto queste informazioni che la firma di fmt.Println non è cambiata in oltre 6 anni? Hai controllato il codice sorgente?
Shahriar


Il commento sull'operazione suggerisce che utilizzare flag.Args()esclusivamente come argomento per fmt.Printlntornare indietro a quel tempo. (Sarebbe sorprendente se non lo facesse in quel momento)
leaf bebop

Così? Se esiste un commento, significa che la mia risposta dovrebbe essere rifiutata?
Shahriar
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.