无法将[]字符串转换为[]接口{}

我正在写一些代码,我需要它来捕捉参数并通过 fmt.Println传递它们
(我希望它的默认行为是用空格分隔参数,后面跟一个换行符)。然而,它需要 []interface {},但是 flag.Args()返回一个 []string
下面是代码示例:

package main


import (
"fmt"
"flag"
)


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

This returns the following error:

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

这是一个错误吗? fmt.Println不应该采用 any数组吗? 顺便说一下,我也尝试过这样做:

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

but I get the following error:

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

有办法解决这个问题吗?

64458 次浏览

这不是窃听器。fmt.Println()需要 []interface{}类型。这意味着,它必须是 interface{}值的一个片,而不是“任何片”。为了转换切片,您需要循环并复制每个元素。

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

不能使用任何切片的原因是,[]string[]interface{}之间的转换需要更改内存布局,并且需要在 O (n)时间内完成。将类型转换为 interface{}需要 O (1)时间。如果他们使 for 循环变得不必要,编译器仍然需要插入它。

如果它只是你想要打印的字符串的一部分,你可以避免转换并且通过加入以下内容获得完全相同的输出:

package main


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


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

fmt.Println可变参数

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

打印 flag.Args()而不转换成 []interface{}是可能的

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

在这种情况下,类型转换是不必要的。只需将 flag.Args()值传递给 fmt.Println


Question:

无法将[]字符串转换为[]接口{}

我正在写一些代码,我需要它来捕捉参数并通过 通过 fmt.Println (我希望它的默认行为是写 用空格分隔并后跟换行符的参数)。

Here's the code example:

package main


import (
"fmt"
"flag"
)


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

包裹旗

import "flag"

方克阿格斯

func Args() []string

Args返回非标志命令行参数。


Package fmt

import "fmt"

放克 · 普林特林

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

使用操作数的默认格式和 写入标准输出。操作数之间总是添加空格 and a newline is appended. It returns the number of bytes written and 遇到任何写入错误。


在这种情况下,类型转换是不必要的。只需将 flag.Args()值传递给 fmt.Println,后者使用反射将值解释为 []string类型。包 reflect实现了运行时反射,允许程序操作具有任意类型的对象。比如说,

返回文章页面

package main


import (
"flag"
"fmt"
)


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

产出:

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

在 Go 中,函数只能接受函数定义中的参数列表中指定的类型的参数。可变参数语言特性使这一点有点复杂,但它遵循定义良好的规则。

fmt.Println的函数签名是:

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

根据 语言规格,

函数签名中的最后一个传入参数可能有一个前缀为... 的类型。具有这种参数的函数称为可变参数,可以用该参数的零个或多个参数调用该函数。

这意味着可以向 Println传递 interface{}类型的参数列表。因为所有类型都实现了空接口,所以您可以传递任何类型的参数列表,这就是您如何能够调用 Println(1, "one", true)而不出错的原因。请参阅语言规范的 “向... 参数传递参数”部分:

the value passed is a new slice of type []T with a new underlying array whose successive elements are the actual arguments, which all must be assignable to T.

The part that's giving you trouble is right after that in the specification:

If the final argument is assignable to a slice type []T, it may be passed unchanged as the value for a ...T parameter if the argument is followed by .... In this case no new slice is created.

flag.Args() is type []string. Since T in Println is interface{}, []T is []interface{}. So the question comes down to whether a string slice value is assignable to a variable of interface slice type. You can easily test that in your go code by attempting an assignment, for example:

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

如果尝试这样分配,编译器将输出以下错误消息:

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

这就是为什么不能使用字符串切片后的省略号作为 fmt.Println的参数。这不是一个错误,它的工作原理。

仍然有很多方法可以用 Println打印 flag.Args(),例如

fmt.Println(flag.Args())

(每个 Fmt 包文档输出为 [elem0 elem1 ...])

or

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

例如,使用带字符串分隔符的 字符串包中的 Join 函数(它将输出字符串片元素,每个元素由一个空格分隔)。

我认为使用反射是可能的,但我不知道这是否是一个好的解决方案

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

产出:

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

另一种选择是只迭代切片:

package main
import "flag"


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