No se puede convertir [] cadena a [] interfaz {}


96

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.Printlntomar 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?


1
Estaba jugando con un ejemplo simple ( go run test.go some test flags), y parecía funcionar cuando cambiaba flags.Args()...a solo flag.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 :)
RocketDonkey

Respuestas:


116

Esto no es un error. fmt.Println()requiere un []interface{}tipo. Eso significa que debe ser una porción de interface{}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 []stringa []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 un interface{}requiere O (1) tiempo. Si hicieran innecesario este bucle for, el compilador aún necesitaría insertarlo.


2
Por cierto, encontré este enlace en golang-nuts: groups.google.com/d/topic/golang-nuts/Il-tO1xtAyE/discussion
cruizh

2
Sí, cada iteración requiere O (1) tiempo y el ciclo requiere O (n) tiempo. Eso es lo que dije. En cuanto a la función que recibe a []string, espera a interface{}. An interface{}tiene un diseño de memoria diferente al de a, stringpor lo que el problema es que cada elemento debe convertirse.
Stephen Weinberg

1
@karlrh: No, suponga que la Printlnfunción modifica el segmento y establece algunos elementos (no lo hace, pero suponga que lo hace). Luego puede poner cualquiera interface{}en la rebanada, que solo debería tener strings. Lo que realmente desea es algo como el comodín de Java Generics Slice<? extends []interface{}>, pero eso no existe en Go.
newacct

4
Agregar es mágico. Está integrado y tratado especialmente por el idioma. Otros ejemplos incluyen new(), len()y copy(). golang.org/ref/spec#Appending_and_copying_slices
Stephen Weinberg

1
¡Hoy aprendí que 'nuevo' no es una palabra clave de reserva! ¡Tampoco lo es hacer!
Sridhar

11

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

11

En este caso, no es necesaria una conversión de tipo. Simplemente pase el flag.Args()valor a fmt.Println.


Pregunta:

No se puede convertir [] cadena a [] interfaz {}

Estoy escribiendo un código y lo necesito para capturar los argumentos y pasarlos a través de fmt.Println (quiero su comportamiento predeterminado, escribir argumentos separados por espacios y seguidos por una nueva línea).

Aquí está el ejemplo de código:

package main

import (
    "fmt"
    "flag"
)

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

Bandera del paquete

import "flag"

func Args

func Args() []string

Args devuelve los argumentos de la línea de comandos sin bandera.


Paquete fmt

import "fmt"

func Println

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

Printlnformatos que utilizan los formatos predeterminados para sus operandos y escribe en la salida estándar. Siempre se agregan espacios entre operandos y se agrega una nueva línea. Devuelve el número de bytes escritos y cualquier error de escritura encontrado.


En este caso, no es necesaria una conversión de tipo. Simplemente pase el flag.Args()valor a fmt.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]
$ 

Si quisiera omitir los corchetes, ¿la conversión aún sería innecesaria?
Cruizh

1

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.Printlnes:

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

Según la especificación del idioma ,

El parámetro entrante final en la firma de una función puede tener un tipo con el prefijo .... Una función con dicho parámetro se llama variadic y puede invocarse con cero o más argumentos para ese parámetro.

Esto significa que puede pasar Printlnuna lista de argumentos de interface{}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 llamar Println(1, "one", true), por ejemplo, sin error. Consulte la sección "Pasar argumentos a ... parámetros" de la especificación del idioma:

el valor pasado es un nuevo segmento de tipo [] T con una nueva matriz subyacente cuyos elementos sucesivos son los argumentos reales, todos los cuales deben ser asignables a T.

La parte que le está dando problemas está justo después de eso en la especificación:

Si el argumento final se puede asignar a un tipo de sector [] T, se puede pasar sin cambios como el valor de un parámetro ... T si el argumento va seguido de .... En este caso, no se crea un nuevo sector.

flag.Args()es tipo []string. Dado que Ten Printlnes interface{}, []Tes []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()con Println, como

fmt.Println(flag.Args())

(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.


0

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}

-1

fmt.Printlntoma parámetro variadic

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

Es posible imprimir flag.Args()sin convertir a[]interface{}

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

2
fmt.PrintlnLa firma no ha cambiado en más de 6 años (y eso fue solo un cambio de paquete error). 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ó.
Marc

¿Cómo consiguió esta información que la firma de fmt.Println no ha cambiado en más de 6 años? ¿Verificó el código fuente?
Shahriar


El comentario sobre la operación sugiere que el uso flag.Args()únicamente como argumento fmt.Printlnfuncionó hacia atrás en ese momento. (Sería sorprendente si no fuera así en ese momento)
leaf bebop

¿Entonces? Si hay un comentario, ¿eso significa que mi respuesta debería ser rechazada?
Shahriar
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.