111 votes

Impossible de convertir []string en []interface {}

J'écris un code, et j'ai besoin qu'il récupère les arguments et les passe à travers fmt.Println
(Je veux son comportement par défaut, pour écrire les arguments séparés par des espaces et suivis d'une nouvelle ligne). Cependant, il 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 ? Ne devrait-on pas fmt.Println prendre tout tableau ? Au fait, j'ai également essayé de faire cela :

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

mais je reçois l'erreur suivante :

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

Existe-t-il un moyen de contourner ce problème ?

133voto

Stephen Weinberg Points 12682

Ce n'est pas un bug. fmt.Println() nécessite un []interface{} type. Cela signifie qu'il doit s'agir d'une tranche de interface{} et non "n'importe quelle tranche". Afin de convertir la tranche, vous devrez boucler et 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 pas utiliser n'importe quelle tranche est que la conversion entre un []string et un []interface{} nécessite de modifier la disposition de la mémoire et se produit en O(n) temps. La conversion d'un type en un interface{} nécessite un temps O(1). S'ils rendaient cette boucle for inutile, le compilateur devrait quand même l'insérer.

13voto

peterSO Points 25725

Dans ce cas, une conversion de type est inutile. Il suffit de passer le flag.Args() à la valeur fmt.Println .


Question :

Impossible de convertir []string en []interface {}

Je suis en train d'écrire un code, et j'ai besoin qu'il attrape les arguments et les transmette les passer par fmt.Println (je veux que son comportement par défaut, d'écrire les les arguments séparés par des espaces et suivis d'une nouvelle ligne).

Voici l'exemple de code :

package main

import (
    "fmt"
    "flag"
)

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

Drapeau du paquet

import "flag"

func Args

func Args() []string

Args retourne les arguments de la ligne de commande sans drapeau.


Paquet fmt

import "fmt"

func Println

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

Println en utilisant les formats par défaut pour ses opérandes et les formats écrit sur la sortie standard. Les espaces sont toujours ajoutés entre les opérandes et une nouvelle ligne est ajoutée. Elle retourne le nombre d'octets écrits et toute erreur d'écriture rencontrée.


Dans ce cas, une conversion de type est inutile. Il suffit de passer le flag.Args() à la valeur fmt.Println qui utilise la réflexion pour interpréter la valeur comme le type []string . Paquet reflect implémente la réflexion au moment de l'exécution, permettant à un programme de manipuler des objets de types arbitraires. Par exemple,

args.go :

package main

import (
    "flag"
    "fmt"
)

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

Sortie :

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

11voto

Zippoxer Points 2271

Si vous ne souhaitez imprimer qu'une tranche de chaînes de caractères, vous pouvez éviter la conversion et obtenir exactement la même sortie en joignant les deux :

package main

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

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

0voto

jrefior Points 2068

En Go, une fonction ne peut accepter que les arguments des types spécifiés dans la liste des paramètres de la définition de la fonction. La fonction de langage des paramètres variadiques complique un peu les choses, mais elle suit des règles bien définies.

La signature de la fonction pour fmt.Println est :

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

Par le spécification de la langue ,

Le dernier paramètre entrant dans la signature d'une fonction peut avoir un type préfixé par ..... Une fonction avec un tel paramètre est dite variadique et peut être invoquée avec zéro ou plusieurs arguments pour ce paramètre.

Cela signifie que vous pouvez passer Println une liste d'arguments de interface{} type. Puisque tous les types implémentent l'interface empty, vous pouvez passer une liste d'arguments de n'importe quel type, c'est ainsi que vous pouvez appeler Println(1, "one", true) par exemple, sans erreur. Voir le Section "Passer des arguments aux paramètres ...". de la spécification du langage :

la valeur passée 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 les spécifications :

Si l'argument final est assignable à une tranche de type []T, il peut être transmis inchangé comme valeur pour un paramètre ...T si l'argument est suivi de ..... Dans ce cas, aucune nouvelle tranche n'est créée.

flag.Args() est le type []string . Puisque T en Println es interface{} , []T es []interface{} . La question se résume donc à savoir si une valeur de tranche de chaîne est assignable à une variable de type tranche d'interface. Vous pouvez facilement tester cela dans votre code go en tentant une affectation, par exemple :

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

Si vous tentez une telle affectation, le compilateur affichera ce message d'erreur :

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

Et c'est pourquoi vous ne pouvez pas utiliser le point de suspension après une tranche de chaîne de caractères comme argument à fmt.Println . Ce n'est pas un bug, il fonctionne comme prévu.

Il existe encore de nombreuses façons d'imprimer flag.Args() con Println comme

fmt.Println(flag.Args())

(qui sera affiché comme [elem0 elem1 ...] par documentation du paquet fmt )

o

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

(qui produira les éléments de la tranche de chaîne, chacun étant séparé par un espace) en utilisant la fonction Join du paquet strings avec un séparateur de chaîne, par exemple.

0voto

Steven Penny Points 18523

Une autre option consiste à simplement itérer la tranche :

package main
import "flag"

func main() {
   flag.Parse()
   for _, each := range flag.Args() {
      println(each)
   }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X