英文:
Cannot convert []string to []interface {}
问题
我正在编写一些代码,我需要它捕获参数并通过fmt.Println
传递它们(我希望它的默认行为是用空格分隔参数并在末尾加上换行符)。然而,它接受[]interface {}
类型,而flag.Args()
返回的是[]string
类型。
以下是代码示例:
package main
import (
"fmt"
"flag"
)
func main() {
flag.Parse()
fmt.Println(flag.Args()...)
}
这会返回以下错误:
./example.go:10: cannot use args (type []string) as type []interface {} in function argument
这是一个bug吗?fmt.Println
难道不应该接受任何数组吗?顺便说一下,我也尝试过这样做:
var args = []interface{}(flag.Args())
但是我得到了以下错误:
cannot convert flag.Args() (type []string) to type []interface {}
有没有一种“Go”的方法来解决这个问题?
英文:
I'm writing some code, and I need it to catch the arguments and pass them through fmt.Println
(I want its default behaviour, to write arguments separated by spaces and followed by a newline). However it takes []interface {}
but flag.Args()
returns a []string
.
Here's the code example:
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
Is this a bug? Shouldn't fmt.Println
take any array? By the way, I've also tried to do this:
var args = []interface{}(flag.Args())
but I get the following error:
cannot convert flag.Args() (type []string) to type []interface {}
Is there a "Go" way to workaround this?
答案1
得分: 139
这不是一个bug。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循环变得不必要,编译器仍然需要插入它。
英文:
This is not a bug. fmt.Println()
requires a []interface{}
type. That means, it must be a slice of interface{}
values and not "any slice". In order to convert the slice, you will need to loop over and copy each element.
old := flag.Args()
new := make([]interface{}, len(old))
for i, v := range old {
new[i] = v
}
fmt.Println(new...)
The reason you can't use any slice is that conversion between a []string
and a []interface{}
requires the memory layout to be changed and happens in O(n) time. Converting a type to an interface{}
requires O(1) time. If they made this for loop unnecessary, the compiler would still need to insert it.
答案2
得分: 14
在这种情况下,类型转换是不必要的。只需将flag.Args()
的值传递给fmt.Println
即可。
> 问题:
>
> 无法将[]string转换为[]interface{}
>
> 我正在编写一些代码,我需要它捕获参数并通过fmt.Println传递它们(我希望它的默认行为是用空格分隔参数,然后换行)。
>
> 这是代码示例:
>
> package main
>
> import (
> "fmt"
> "flag"
> )
>
> func main() {
> flag.Parse()
> fmt.Println(flag.Args()...)
> }
> Package flag
>
> import "flag"
>
> func Args
>
> func Args() []string
>
> Args
返回非标志命令行参数。
> Package fmt
>
> import "fmt"
>
> func Println
>
> func Println(a ...interface{}) (n int, err error)
>
> Println
使用其操作数的默认格式进行格式化,并写入标准输出。操作数之间始终添加空格,并追加换行符。它返回写入的字节数和遇到的任何写入错误。
在这种情况下,类型转换是不必要的。只需将flag.Args()
的值传递给fmt.Println
,它使用反射将该值解释为[]string
类型。包reflect
实现了运行时反射,允许程序操作具有任意类型的对象。例如,
args.go
:
package main
import (
"flag"
"fmt"
)
func main() {
flag.Parse()
fmt.Println(flag.Args())
}
输出:
$ go build args.go
$ ./args arg0 arg1
[arg0 arg1]
$
英文:
In this case, a type conversion is unnecessary. Simply pass the flag.Args()
value to fmt.Println
.
> Question:
>
> Cannot convert []string to []interface {}
>
> I'm writing some code, and I need it to catch the arguments and pass
> them through fmt.Println (I want its default behaviour, to write
> arguments separated by spaces and followed by a newline).
>
> Here's the code example:
>
> package main
>
> import (
> "fmt"
> "flag"
> )
>
> func main() {
> flag.Parse()
> fmt.Println(flag.Args()...)
> }
> Package flag
>
> import "flag"
>
> func Args
>
> func Args() []string
>
> Args
returns the non-flag command-line arguments.
> Package fmt
>
> import "fmt"
>
> func Println
>
> func Println(a ...interface{}) (n int, err error)
>
> Println
formats using the default formats for its operands and
> writes to standard output. Spaces are always added between operands
> and a newline is appended. It returns the number of bytes written and
> any write error encountered.
In this case, a type conversion is unnecessary. Simply pass the flag.Args()
value to fmt.Println
, which uses reflection to interpret the value as type []string
. Package reflect
implements run-time reflection, allowing a program to manipulate objects with arbitrary types. For example,
args.go
:
package main
import (
"flag"
"fmt"
)
func main() {
flag.Parse()
fmt.Println(flag.Args())
}
Output:
$ go build args.go
$ ./args arg0 arg1
[arg0 arg1]
$
答案3
得分: 11
如果你只想打印字符串的一部分,你可以避免转换并通过连接来获得完全相同的输出:
package main
import (
"fmt"
"flag"
"strings"
)
func main() {
flag.Parse()
s := strings.Join(flag.Args(), " ")
fmt.Println(s)
}
英文:
If it's only a slice of strings you want to print, you can avoid conversion and get the exact same output by joining:
package main
import (
"fmt"
"flag"
"strings"
)
func main() {
flag.Parse()
s := strings.Join(flag.Args(), " ")
fmt.Println(s)
}
答案4
得分: 0
在Go中,函数只能接受函数定义中参数列表中指定的类型的参数。可变参数语言特性使得这有点复杂,但它遵循明确定义的规则。
fmt.Println
的函数签名是:
func Println(a ...interface{}) (n int, err error)
根据语言规范:
> 函数签名中的最后一个传入参数可以有一个以...为前缀的类型。具有这种参数的函数被称为可变参数函数,可以使用零个或多个参数调用该函数。
这意味着您可以传递Println
一个interface{}
类型的参数列表。由于所有类型都实现了空接口,因此您可以传递任何类型的参数列表,这就是为什么您可以无错误地调用Println(1, "one", true)
的原因。请参阅语言规范中的“传递参数给...参数”部分:
> 传递的值是一个新的类型为[]T的切片,其底层数组是实际参数,所有参数都必须可分配给T。
让您感到困惑的部分就在规范中紧随其后:
> 如果最后一个参数可分配给切片类型[]T,则如果参数后跟...,则可以将其不变地作为...T参数的值传递。在这种情况下,不会创建新的切片。
flag.Args()
的类型是[]string
。由于Println
中的T
是interface{}
,所以[]T
是[]interface{}
。所以问题归结为字符串切片值是否可分配给接口切片类型的变量。您可以在您的Go代码中通过尝试赋值来轻松测试这一点,例如:
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 ...]
)
或者
fmt.Println(strings.Join(flag.Args(), ` `)
(使用字符串分隔符,例如字符串包中的Join函数)将输出字符串切片元素,每个元素之间用一个空格分隔。
英文:
In Go, a function can only accept arguments of the types specified in the parameter list in the function definition. The variadic parameter language feature complicates that a bit, but it follows well-defined rules.
The function signature for fmt.Println
is:
func Println(a ...interface{}) (n int, err error)
Per the language specifiction,
> The final incoming parameter in a function signature may have a type prefixed with .... A function with such a parameter is called variadic and may be invoked with zero or more arguments for that parameter.
This means you can pass Println
a list of arguments of interface{}
type. Since all types implement the empty interface, you can pass a list of arguments of any type, which is how you're able to call Println(1, "one", true)
, for example, without error. See the "Passing arguments to ... parameters" section of the language specification:
> 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
If you attempt such an assignment, the compiler will output this error message:
cannot use s (type []string) as type []interface {} in assignment
And that's why you can't use the ellipsis after a string slice as an argument to fmt.Println
. It's not a bug, it's working as intended.
There are still lots of ways you can print flag.Args()
with Println
, such as
fmt.Println(flag.Args())
(which will output as [elem0 elem1 ...]
, per fmt package documentation)
or
fmt.Println(strings.Join(flag.Args(), ` `)
(which will output the string slice elements, each separated by a single space) using the Join function in the strings package with a string separator, for example.
答案5
得分: 0
我认为可以使用反射来实现,但我不知道这是否是一个好的解决方案。
package main
import (
"fmt"
"reflect"
"strings"
)
type User struct {
Name string
Age byte
}
func main() {
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)
}
输出:
hello,world
1,2,3,4,5,6
{John 30},{Not John 20}
英文:
I think it's possible using reflection, but I don't know if it's a good solution
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)
}
Output:
$ go run .\main.go arg1 arg2
arg1,arg2
hello,world
1,2,3,4,5,6
{John 30},{Not John 20}
答案6
得分: 0
另一个选项是只迭代切片:
英文:
Another option is to just iterate the slice:
package main
import "flag"
func main() {
flag.Parse()
for _, each := range flag.Args() {
println(each)
}
}
答案7
得分: -2
fmt.Println
接受可变参数。
> func Println(a ...interface{}) (n int, err error)
可以直接打印flag.Args()
而不需要转换为[]interface{}
func main() {
flag.Parse()
fmt.Println(flag.Args())
}
英文:
fmt.Println
takes variadic parameter
> func Println(a ...interface{}) (n int, err error)
Its possible to print flag.Args()
without converting into []interface{}
func main() {
flag.Parse()
fmt.Println(flag.Args())
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论