[] String kann nicht in [] Schnittstelle {} konvertiert werden


92

Ich schreibe Code, und ich brauche ihn, um die Argumente fmt.Println
abzufangen und durchzuleiten (ich möchte das Standardverhalten, um durch Leerzeichen getrennte Argumente zu schreiben, gefolgt von einer neuen Zeile). Es dauert []interface {}jedoch aber flag.Args()gibt a zurück []string.
Hier ist das Codebeispiel:

package main

import (
    "fmt"
    "flag"
)

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

Dies gibt den folgenden Fehler zurück:

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

Ist das ein Fehler? Sollte nicht fmt.Printlnnehmen jede Array? Übrigens habe ich auch versucht, dies zu tun:

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

aber ich bekomme folgenden Fehler:

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

Gibt es einen "Go" Weg, um dies zu umgehen?


1
Ich habe mit einem einfachen Beispiel ( go run test.go some test flags) herumgespielt , und es schien zu funktionieren, wenn flags.Args()...auf just gewechselt wurde flag.Args()(Ausgabe ist [some test flags], gefolgt von der neuen Zeile; schien auch mit der Registrierung tatsächlicher Flags zu funktionieren).
Ich

Antworten:


110

Dies ist kein Fehler. fmt.Println()erfordert einen []interface{}Typ. Das heißt, es muss ein Werteschnitt sein interface{}und nicht "irgendein Teil". Um das Slice zu konvertieren, müssen Sie jedes Element durchlaufen und kopieren.

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

Der Grund, warum Sie kein Slice verwenden können, ist, dass für die Konvertierung zwischen a []stringund a []interface{}das Speicherlayout geändert werden muss und dies in O (n) -Zeit erfolgt. Das Konvertieren eines Typs in einen Typ interface{}erfordert O (1) Zeit. Wenn sie diese for-Schleife unnötig machen würden, müsste der Compiler sie trotzdem einfügen.


2
Übrigens habe ich diesen Link in Golang-Nuts gefunden: groups.google.com/d/topic/golang-nuts/Il-tO1xtAyE/discussion
cruizh

2
Ja, jede Iteration benötigt O (1) Zeit und die Schleife benötigt O (n) Zeit. Das ist, was ich gesagt habe. Die Funktion, die a empfängt []string, erwartet a interface{}. An interface{}hat ein anderes Speicherlayout als a, stringdaher ist die Tatsache, dass jedes Element konvertiert werden muss, das Problem.
Stephen Weinberg

1
@karlrh: Nein, angenommen, die PrintlnFunktion ändert das Slice und setzt einige Elemente (dies ist nicht der Fall, aber es ist der Fall). Dann kann es jedes interface{}in die Scheibe legen , die nur strings haben sollte. Was Sie wirklich wollen, ist so etwas wie der Java Generics-Platzhalter Slice<? extends []interface{}>, aber das gibt es in Go nicht.
Newacct

4
Anhängen ist magisch. Es ist eingebaut und wird speziell von der Sprache behandelt. Weitere Beispiele sind new(), len()und copy(). golang.org/ref/spec#Appending_and_copying_slices
Stephen Weinberg

1
Heute habe ich gelernt, dass 'neu' kein Schlüsselwort ist! Noch ist machen!
Sridhar

10

Wenn es sich nur um ein Stück Zeichenfolgen handelt, die Sie drucken möchten, können Sie die Konvertierung vermeiden und durch Verbinden genau dieselbe Ausgabe erhalten:

package main

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

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

10

In diesem Fall ist eine Typkonvertierung nicht erforderlich. Übergeben Sie einfach den flag.Args()Wert an fmt.Println.


Frage:

[] String kann nicht in [] Schnittstelle {} konvertiert werden

Ich schreibe Code, und ich brauche ihn, um die Argumente abzufangen und durch fmt.Println zu übergeben (ich möchte sein Standardverhalten, um Argumente zu schreiben, die durch Leerzeichen getrennt sind und von einer neuen Zeile gefolgt werden).

Hier ist das Codebeispiel:

package main

import (
    "fmt"
    "flag"
)

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

Paketflagge

import "flag"

func Args

func Args() []string

Args Gibt die Nicht-Flag-Befehlszeilenargumente zurück.


Paket fmt

import "fmt"

func Println

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

PrintlnFormate, die die Standardformate für ihre Operanden verwenden und in die Standardausgabe schreiben. Zwischen Operanden werden immer Leerzeichen hinzugefügt und eine neue Zeile angehängt. Es gibt die Anzahl der geschriebenen Bytes und alle aufgetretenen Schreibfehler zurück.


In diesem Fall ist eine Typkonvertierung nicht erforderlich. Übergeben Sie einfach den flag.Args()Wert an fmt.Println, der den Wert mithilfe der Reflexion als Typ interpretiert []string. Das Paket reflectimplementiert die Laufzeitreflexion, sodass ein Programm Objekte mit beliebigen Typen bearbeiten kann. Beispielsweise,

args.go::

package main

import (
    "flag"
    "fmt"
)

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

Ausgabe:

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

Wenn ich die Klammern weglassen wollte, wäre die Konvertierung dann immer noch unnötig?
Cruizh

0

In Go kann eine Funktion nur Argumente der in der Parameterliste in der Funktionsdefinition angegebenen Typen akzeptieren. Die Sprachfunktion für verschiedene Parameter erschwert dies ein wenig, folgt jedoch genau definierten Regeln.

Die Funktionssignatur für fmt.Printlnlautet:

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

Per der Sprache specific ,

Der letzte eingehende Parameter in einer Funktionssignatur kann einen Typ haben, dem ... vorangestellt ist. Eine Funktion mit einem solchen Parameter wird als variadisch bezeichnet und kann mit null oder mehr Argumenten für diesen Parameter aufgerufen werden.

Dies bedeutet, dass Sie Printlneine Liste von Argumenten vom interface{}Typ übergeben können. Da alle Typen die leere Schnittstelle implementieren, können Sie eine Liste von Argumenten eines beliebigen Typs übergeben. So können Sie Println(1, "one", true)beispielsweise fehlerfrei aufrufen . Siehe den Abschnitt "Übergeben von Argumenten an ... Parameter" der Sprachspezifikation:

Der übergebene Wert ist eine neue Schicht vom Typ [] T mit einem neuen zugrunde liegenden Array, dessen aufeinanderfolgende Elemente die tatsächlichen Argumente sind, die alle T zugewiesen werden müssen.

Der Teil, der Ihnen Probleme bereitet, steht direkt danach in der Spezifikation:

Wenn das endgültige Argument einem Slice-Typ [] T zugewiesen werden kann, kann es unverändert als Wert für einen ... T-Parameter übergeben werden, wenn auf das Argument ... folgt. In diesem Fall wird kein neues Slice erstellt.

flag.Args()ist Typ []string. Da Tin Printlnist interface{}, []Tist []interface{}. Es stellt sich also die Frage, ob ein String-Slice-Wert einer Variablen vom Schnittstellen-Slice-Typ zugewiesen werden kann. Sie können dies einfach in Ihrem Go-Code testen, indem Sie eine Zuweisung versuchen, zum Beispiel:

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

Wenn Sie eine solche Zuweisung versuchen, gibt der Compiler die folgende Fehlermeldung aus:

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

Und deshalb können Sie die Auslassungspunkte nach einem String-Slice nicht als Argument dafür verwenden fmt.Println. Es ist kein Fehler, es funktioniert wie beabsichtigt.

Es gibt immer noch viele Möglichkeiten , können Sie drucken flag.Args()mit Println, wie

fmt.Println(flag.Args())

(wird [elem0 elem1 ...]gemäß der Dokumentation des FMT-Pakets ausgegeben )

oder

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

(wodurch die String-Slice-Elemente ausgegeben werden, die jeweils durch ein einzelnes Leerzeichen getrennt sind) Verwenden Sie beispielsweise die Join-Funktion im Strings-Paket mit einem String-Trennzeichen.


0

Ich denke, es ist möglich, Reflexion zu verwenden, aber ich weiß nicht, ob es eine gute Lösung ist

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

Ausgabe:

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

-1

fmt.Printlnnimmt variadic Parameter

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

Es ist möglich zu drucken, flag.Args()ohne in zu konvertieren[]interface{}

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

2
fmt.PrintlnDie Unterschrift hat sich seit über 6 Jahren nicht geändert (und das war nur eine Paketänderung für error). Selbst wenn dies der Fall wäre, heißt es in der Spezifikation eindeutig "variadisch mit einem endgültigen Parameter p vom Typ ... T". Innerhalb von f entspricht der Typ von p dem Typ [] T. ", sodass dies keine Rolle spielt. fmt.Println(flags.Args()...)funktioniert immer noch nicht (Sie vermissen die Slice-Erweiterung), hat fmt.Println(flags.Args())immer funktioniert.
Marc

Wie haben Sie diese Information erhalten, dass sich die Signatur von fmt.Println seit über 6 Jahren nicht geändert hat? Haben Sie den Quellcode überprüft?
Shahriar


Der Kommentar zur Operation deutet darauf hin, dass flag.Args()nur diese Zeit als Argument verwendet wurde, fmt.Printlnum diese Zeit zurück zu arbeiten. (Es wäre überraschend, wenn es zu diesem Zeitpunkt nicht wäre)
Blatt Bebop

So? Wenn es einen Kommentar gibt, bedeutet das, dass meine Antwort abgelehnt werden sollte?
Shahriar
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.