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.Println
ter 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?
type-conversion
go
Cruizh
fonte
fonte
go run test.go some test flags
), e parecia funcionar ao mudarflags.Args()...
para apenasflag.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 :)Respostas:
Este não é um bug.
fmt.Println()
requer um[]interface{}
tipo. Isso significa que deve ser uma fatia deinterface{}
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
[]string
e a[]interface{}
requer que o layout da memória seja alterado e acontece no tempo O (n). Converter um tipo eminterface{}
requer tempo O (1). Se eles tornassem esse loop for desnecessário, o compilador ainda precisaria inseri-lo.fonte
[]string
, ela espera ainterface{}
. Uminterface{}
tem um layout de memória diferente de um,string
portanto, o problema é o fato de que cada elemento precisa ser convertido.Println
função modifique a fatia e defina alguns elementos (não altera, mas suponha que sim). Em seguida, ele pode colocar qualquerinterface{}
na fatia, que deve ter apenasstring
s. O que você realmente quer é algo como o curinga Java GenericsSlice<? extends []interface{}>
, mas isso não existe no Go.new()
,len()
ecopy()
. golang.org/ref/spec#Appending_and_copying_slicesSe 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) }
fonte
Nesse caso, uma conversão de tipo é desnecessária. Basta passar o
flag.Args()
valor parafmt.Println
.Nesse caso, uma conversão de tipo é desnecessária. Basta passar o
flag.Args()
valor parafmt.Println
, que usa reflexão para interpretar o valor como tipo[]string
. Packagereflect
implementa 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] $
fonte
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 ,
Isso significa que você pode passar
Println
uma lista de argumentos dointerface{}
tipo. Já que todos os tipos implementam a interface vazia, você pode passar uma lista de argumentos de qualquer tipo, que é como você pode chamarPrintln(1, "one", true)
, por exemplo, sem erros. Consulte a seção "Passando argumentos para ... parâmetros" da especificação do idioma:A parte que está causando problemas está logo depois na especificação:
flag.Args()
é tipo[]string
. Já queT
emPrintln
é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()
comPrintln
, tais como(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.
fonte
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}
fonte
fmt.Println
leva parâmetro variadicÉ possível imprimir
flag.Args()
sem converter em[]interface{}
func main() { flag.Parse() fmt.Println(flag.Args()) }
fonte
fmt.Println
a assinatura de não mudou em mais de 6 anos (e isso foi apenas uma mudança de pacote paraerror
). 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.flag.Args()
apenas como argumentofmt.Println
funcionou naquela época. (Seria surpreendente se não acontecesse naquela época)