Estoy escribiendo algo de código y lo necesito para capturar los argumentos y pasarlos fmt.Println
(quiero su comportamiento predeterminado, escribir argumentos separados por espacios y seguidos por una nueva línea). Sin embargo, toma []interface {}
pero flag.Args()
devuelve un []string
.
Aquí está el ejemplo de código:
package main
import (
"fmt"
"flag"
)
func main() {
flag.Parse()
fmt.Println(flag.Args()...)
}
Esto devuelve el siguiente error:
./example.go:10: cannot use args (type []string) as type []interface {} in function argument
¿Es esto un error? ¿No debería fmt.Println
tomar ninguna matriz? Por cierto, también intenté hacer esto:
var args = []interface{}(flag.Args())
pero me sale el siguiente error:
cannot convert flag.Args() (type []string) to type []interface {}
¿Existe una forma "Go" de solucionar esto?
type-conversion
go
cruizh
fuente
fuente
go run test.go some test flags
), y parecía funcionar cuando cambiabaflags.Args()...
a soloflag.Args()
(la salida es[some test flags]
, seguida de la nueva línea; también parecía funcionar con el registro de banderas reales). No se pretende comprender por qué, y la respuesta de Stephen es de todos modos mucho más informativo :)Respuestas:
Esto no es un error.
fmt.Println()
requiere un[]interface{}
tipo. Eso significa que debe ser una porción deinterface{}
valores y no "cualquier porción". Para convertir el sector, deberá recorrer y copiar cada elemento.old := flag.Args() new := make([]interface{}, len(old)) for i, v := range old { new[i] = v } fmt.Println(new...)
La razón por la que no puede usar ningún segmento es que la conversión entre ay
[]string
a[]interface{}
requiere que se cambie el diseño de la memoria y ocurre en el tiempo O (n). La conversión de un tipo a uninterface{}
requiere O (1) tiempo. Si hicieran innecesario este bucle for, el compilador aún necesitaría insertarlo.fuente
[]string
, espera ainterface{}
. Aninterface{}
tiene un diseño de memoria diferente al de a,string
por lo que el problema es que cada elemento debe convertirse.Println
función modifica el segmento y establece algunos elementos (no lo hace, pero suponga que lo hace). Luego puede poner cualquierainterface{}
en la rebanada, que solo debería tenerstring
s. Lo que realmente desea es algo como el comodín de Java GenericsSlice<? extends []interface{}>
, pero eso no existe en Go.new()
,len()
ycopy()
. golang.org/ref/spec#Appending_and_copying_slicesSi solo desea imprimir una porción de cadenas, puede evitar la conversión y obtener exactamente el mismo resultado uniendo:
package main import ( "fmt" "flag" "strings" ) func main() { flag.Parse() s := strings.Join(flag.Args(), " ") fmt.Println(s) }
fuente
En este caso, no es necesaria una conversión de tipo. Simplemente pase el
flag.Args()
valor afmt.Println
.En este caso, no es necesaria una conversión de tipo. Simplemente pase el
flag.Args()
valor afmt.Println
, que utiliza la reflexión para interpretar el valor como tipo[]string
. Paquetereflect
implementa la reflexión en tiempo de ejecución, lo que permite que un programa manipule objetos con tipos arbitrarios. Por ejemplo,args.go
:package main import ( "flag" "fmt" ) func main() { flag.Parse() fmt.Println(flag.Args()) }
Salida:
$ go build args.go $ ./args arg0 arg1 [arg0 arg1] $
fuente
En Go, una función solo puede aceptar argumentos de los tipos especificados en la lista de parámetros en la definición de la función. La característica del lenguaje de parámetros variadic complica eso un poco, pero sigue reglas bien definidas.
La firma de la función
fmt.Println
es:func Println(a ...interface{}) (n int, err error)
Según la especificación del idioma ,
Esto significa que puede pasar
Println
una lista de argumentos deinterface{}
tipo. Dado que todos los tipos implementan la interfaz vacía, puede pasar una lista de argumentos de cualquier tipo, que es cómo puede llamarPrintln(1, "one", true)
, por ejemplo, sin error. Consulte la sección "Pasar argumentos a ... parámetros" de la especificación del idioma:La parte que le está dando problemas está justo después de eso en la especificación:
flag.Args()
es tipo[]string
. Dado queT
enPrintln
esinterface{}
,[]T
es[]interface{}
. Entonces, la pregunta se reduce a si un valor de segmento de cadena se puede asignar a una variable de tipo de segmento de interfaz. Puede probarlo fácilmente en su código de Go intentando una tarea, por ejemplo:s := []string{} var i []interface{} i = s
Si intenta tal asignación, el compilador mostrará este mensaje de error:
cannot use s (type []string) as type []interface {} in assignment
Y es por eso que no puede usar la elipsis después de un segmento de cadena como argumento para
fmt.Println
. No es un error, funciona según lo previsto.Hay un montón de maneras aún se pueden imprimir
flag.Args()
conPrintln
, como(que saldrá como
[elem0 elem1 ...]
, según la documentación del paquete fmt )o
fmt.Println(strings.Join(flag.Args(), ` `)
(que generará la salida de los elementos de corte de cadena, cada uno separado por un espacio) usando la función Unir en el paquete de cadenas con un separador de cadena, por ejemplo.
fuente
Creo que es posible usar la reflexión, pero no sé si es una buena solución.
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) }
Salida:
$ go run .\main.go arg1 arg2 arg1,arg2 hello,world 1,2,3,4,5,6 {John 30},{Not John 20}
fuente
fmt.Println
toma parámetro variadicEs posible imprimir
flag.Args()
sin convertir a[]interface{}
func main() { flag.Parse() fmt.Println(flag.Args()) }
fuente
fmt.Println
La firma no ha cambiado en más de 6 años (y eso fue solo un cambio de paqueteerror
). Incluso si lo hubiera hecho, la especificación dice claramente `variadic con un parámetro final p de tipo ... T, entonces dentro de f el tipo de p es equivalente al tipo [] T.` por lo que no importaría.fmt.Println(flags.Args()...)
todavía no funciona (le falta la expansión de corte),fmt.Println(flags.Args())
siempre funcionó.flag.Args()
únicamente como argumentofmt.Println
funcionó hacia atrás en ese momento. (Sería sorprendente si no fuera así en ese momento)