J'écris du code, et j'en ai besoin pour saisir les arguments et les passer à travers fmt.Println
(Je veux son comportement par défaut, écrire des arguments séparés par des espaces et suivis d'une nouvelle ligne). Cependant, cela prend []interface {}
mais flag.Args()
renvoie un []string
.
Voici l'exemple de code:
package main
import (
"fmt"
"flag"
)
func main() {
flag.Parse()
fmt.Println(flag.Args()...)
}
Cela renvoie l'erreur suivante:
./example.go:10: cannot use args (type []string) as type []interface {} in function argument
Est-ce un bug? fmt.Println
ne devrait-il pas prendre any array? A propos, j'ai aussi essayé de faire ça:
var args = []interface{}(flag.Args())
mais j'obtiens l'erreur suivante:
cannot convert flag.Args() (type []string) to type []interface {}
Existe-t-il un moyen de contourner ce problème?
Ce n'est pas un bug. fmt.Println()
nécessite un type []interface{}
. Cela signifie qu'il doit s'agir d'une tranche de valeurs interface{}
et non de "toute tranche". Pour convertir la tranche, vous aurez besoin de faire une boucle et de copier chaque élément.
old := flag.Args()
new := make([]interface{}, len(old))
for i, v := range old {
new[i] = v
}
fmt.Println(new...)
La raison pour laquelle vous ne pouvez utiliser aucune tranche est que la conversion entre un []string
et un []interface{}
nécessite la modification de la structure de la mémoire et est effectuée dans le temps O(n). La conversion d'un type en interface{}
nécessite O(1) temps. S'ils rendaient cette boucle inutile, le compilateur aurait toujours besoin de l'insérer.
Si vous souhaitez imprimer uniquement une chaîne de chaînes, vous pouvez éviter la conversion et obtenir exactement le même résultat en joignant:
package main
import (
"fmt"
"flag"
"strings"
)
func main() {
flag.Parse()
s := strings.Join(flag.Args(), " ")
fmt.Println(s)
}
Je pense qu'il est possible d'utiliser la réflexion, mais je ne sais pas si c'est une bonne solution
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)
}
Sortie:
$ go run .\main.go arg1 arg2
arg1,arg2
hello,world
1,2,3,4,5,6
{John 30},{Not John 20}
Dans Go, une fonction ne peut accepter que les arguments des types spécifiés dans la liste de paramètres de la définition de la fonction. La fonctionnalité de langage de paramètre variadique complique un peu cela, mais elle suit des règles bien définies.
La signature de fonction pour fmt.Println
est:
func Println(a ...interface{}) (n int, err error)
Selon la spécificité linguistique ,
Le dernier paramètre entrant dans une signature de fonction peut avoir un type préfixé avec .... Une fonction avec un tel paramètre est appelée variadique et peut être appelée avec zéro ou plusieurs arguments pour ce paramètre.
Cela signifie que vous pouvez transmettre à Println
une liste d'arguments de type interface{}
. Etant donné que tous les types implémentent l'interface vide, vous pouvez transmettre une liste d'arguments de n'importe quel type, ce qui vous permet d'appeler Println(1, "one", true)
, par exemple, sans erreur. Voir la section "Passage d'arguments à ... paramètres" de la spécification de langue:
la valeur transmise est une nouvelle tranche de type [] T avec un nouveau tableau sous-jacent dont les éléments successifs sont les arguments réels, qui doivent tous être assignables à T.
La partie qui vous pose problème se trouve juste après dans la spécification:
Si l'argument final est assignable à un type de tranche [] T, il peut être transmis sans modification en tant que valeur d'un paramètre ... T si l'argument est suivi de .... Dans ce cas, aucune nouvelle tranche n'est créée.
flag.Args()
est de type []string
. Puisque T
dans Println
est interface{}
, []T
est []interface{}
. La question est donc de savoir si une valeur de tranche de chaîne peut être assignée à une variable de type de tranche d’interface. Vous pouvez facilement tester cela dans votre code d'identification en tentant une affectation, par exemple:
s := []string{}
var i []interface{}
i = s
Si vous tentez une telle affectation, le compilateur générera ce message d'erreur:
cannot use s (type []string) as type []interface {} in assignment
Et c’est pourquoi vous ne pouvez pas utiliser les Ellipsis après une tranche de chaîne comme argument de fmt.Println
. Ce n'est pas un bug, il fonctionne comme prévu.
Vous pouvez toujours imprimer flag.Args()
avec Println
de différentes manières, par exemple
fmt.Println(flag.Args())
(qui sera affiché sous la forme [elem0 elem1 ...]
, par documentation du paquet fmt )
ou
fmt.Println(strings.Join(flag.Args(), ` `)
(qui produira les éléments de la tranche de chaîne, chacun étant séparé par un seul espace) en utilisant la fonction Join dans le package de chaînes avec un séparateur de chaîne, par exemple.