Não é possível converter [] string em [] interface {}


97

Estou escrevendo um código e preciso capturar os argumentos e transmiti-los fmt.Println
(quero o comportamento padrão, escrever argumentos separados por espaços e seguidos por uma nova linha). No entanto, leva, []interface {}mas flag.Args()retorna a []string.
Aqui está o exemplo de código:

package main

import (
    "fmt"
    "flag"
)

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

Isso retorna o seguinte erro:

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

Isso é um inseto? Não deve fmt.Printlnter nenhuma matriz? A propósito, também tentei fazer isso:

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

mas recebo o seguinte erro:

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

Existe uma maneira "Vá" de contornar isso?


1
Eu estava mexendo com um exemplo simples ( go run test.go some test flags), e parecia funcionar ao mudar flags.Args()...para apenas flag.Args()(a saída é [some test flags], seguida pela nova linha; também parecia funcionar com o registro de sinalizadores reais). Não vou fingir que entendi o porquê, e a resposta de Stephen é muito mais informativa de qualquer maneira :)
— RocketDonkey

Respostas:


117

Este não é um bug. fmt.Println()requer um []interface{}tipo. Isso significa que deve ser uma fatia de interface{}valores e não "qualquer fatia". Para converter a fatia, você precisará fazer um loop e copiar cada elemento.

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

O motivo pelo qual você não pode usar nenhuma fatia é que a conversão entre a []stringe a []interface{}requer que o layout da memória seja alterado e acontece no tempo O (n). Converter um tipo em interface{}requer tempo O (1). Se eles tornassem esse loop for desnecessário, o compilador ainda precisaria inseri-lo.


2
A propósito, encontrei este link em golang-nuts: groups.google.com/d/topic/golang-nuts/Il-tO1xtAyE/discussion
— cruizh

2
Sim, cada iteração requer tempo O (1) e o loop requer tempo O (n). Foi isso que eu disse. Quanto à função que recebe a []string, ela espera a interface{}. Um interface{}tem um layout de memória diferente de um, stringportanto, o problema é o fato de que cada elemento precisa ser convertido.
— Stephen Weinberg de

1
@karlrh: Não, suponha que a Printlnfunção modifique a fatia e defina alguns elementos (não altera, mas suponha que sim). Em seguida, ele pode colocar qualquer interface{}na fatia, que deve ter apenas strings. O que você realmente quer é algo como o curinga Java Generics Slice<? extends []interface{}>, mas isso não existe no Go.
— newacct

4
Append é mágico. É embutido e tratado de forma especial pela linguagem. Outros exemplos incluem new(), len()e copy(). golang.org/ref/spec#Appending_and_copying_slices
— Stephen Weinberg

1
Hoje eu aprendi que 'novo' não é uma palavra-chave de reserva! Nem é fazer!
— Sridhar

11

Se for apenas uma parte das strings que deseja imprimir, você pode evitar a conversão e obter exatamente a mesma saída juntando:

package main

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

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

11

Nesse caso, uma conversão de tipo é desnecessária. Basta passar o flag.Args()valor para fmt.Println.


Questão:

Não é possível converter [] string em [] interface {}

Estou escrevendo um código e preciso capturá-los e passá-los por meio de fmt.Println (quero que seu comportamento padrão seja escrever argumentos separados por espaços e seguidos por uma nova linha).

Aqui está o exemplo de código:

package main

import (
    "fmt"
    "flag"
)

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

Bandeira de pacote

import "flag"

func Args

func Args() []string

Args retorna os argumentos de linha de comando não sinalizadores.


Pacote fmt

import "fmt"

função Println

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

Printlnformatos usando os formatos padrão para seus operandos e grava na saída padrão. Espaços são sempre adicionados entre operandos e uma nova linha é acrescentada. Ele retorna o número de bytes gravados e qualquer erro de gravação encontrado.


Nesse caso, uma conversão de tipo é desnecessária. Basta passar o flag.Args()valor para fmt.Println, que usa reflexão para interpretar o valor como tipo []string. Package reflectimplementa reflexão em tempo de execução, permitindo que um programa manipule objetos com tipos arbitrários. Por exemplo,

args.go:

package main

import (
    "flag"
    "fmt"
)

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

Resultado:

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

Se eu quisesse omitir os colchetes, a conversão ainda seria desnecessária?
— cruizh

1

Em Go, uma função só pode aceitar argumentos dos tipos especificados na lista de parâmetros na definição da função. O recurso de linguagem de parâmetro variável complica um pouco isso, mas segue regras bem definidas.

A assinatura da função para fmt.Printlné:

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

De acordo com a especificação do idioma ,

O parâmetro de entrada final em uma assinatura de função pode ter um tipo prefixado com .... Uma função com tal parâmetro é chamada de variadic e pode ser chamada com zero ou mais argumentos para aquele parâmetro.

Isso significa que você pode passar Printlnuma lista de argumentos do interface{}tipo. Já que todos os tipos implementam a interface vazia, você pode passar uma lista de argumentos de qualquer tipo, que é como você pode chamar Println(1, "one", true), por exemplo, sem erros. Consulte a seção "Passando argumentos para ... parâmetros" da especificação do idioma:

o valor passado é uma nova fatia do tipo [] T com uma nova matriz subjacente cujos elementos sucessivos são os argumentos reais, que devem ser atribuídos a T.

A parte que está causando problemas está logo depois na especificação:

Se o argumento final puder ser atribuído a uma fatia do tipo [] T, ele pode ser passado inalterado como o valor para um parâmetro ... T se o argumento for seguido por .... Neste caso, nenhuma nova fatia é criada.

flag.Args()é tipo []string. Já que Tem Printlné interface{}, []Té []interface{}. Portanto, a questão se resume a se um valor de fatia de string pode ser atribuído a uma variável do tipo de fatia de interface. Você pode testar isso facilmente em seu código go tentando uma atribuição, por exemplo:

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

Se você tentar essa atribuição, o compilador produzirá esta mensagem de erro:

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

E é por isso que você não pode usar as reticências após uma fatia de string como um argumento para fmt.Println. Não é um bug, está funcionando conforme o esperado.

Há muitas ainda de maneiras que você pode imprimir flag.Args()com Println, tais como

fmt.Println(flag.Args())

(que sairá como [elem0 elem1 ...], por documentação do pacote fmt )

ou

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

(que produzirá os elementos de fatia de string, cada um separado por um único espaço) usando a função Join no pacote de strings com um separador de string, por exemplo.


0

Acho que é possível usar reflexão, mas não sei se é uma boa solução

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

Resultado:

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

-1

fmt.Printlnleva parâmetro variadic

func Println (a ... interface {}) (n int, erro de erro)

É possível imprimir flag.Args()sem converter em[]interface{}

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

2
fmt.Printlna assinatura de não mudou em mais de 6 anos (e isso foi apenas uma mudança de pacote para error). Mesmo se tivesse, a especificação diz claramente `variadic com um parâmetro final p do tipo ... T, então dentro de f o tipo de p é equivalente ao tipo [] T`, então não faria diferença. fmt.Println(flags.Args()...)ainda não funciona (está faltando a expansão do slice), fmt.Println(flags.Args())sempre funcionou.
— Marc

Como você conseguiu essa informação de que a assinatura de fmt.Println não mudou em mais de 6 anos? Você verificou o código-fonte?
— Shahriar


O comentário sobre a operação sugere que usar flag.Args()apenas como argumento fmt.Printlnfuncionou naquela época. (Seria surpreendente se não acontecesse naquela época)
— leaf bebop

Então? Se houver um comentário, isso significa que minha resposta deve ser rejeitada?
— Shahriar
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.