无法将 []string 转换为 []interface {}

huangapple go评论94阅读模式
英文:

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中的Tinterface{},所以[]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 (
	&quot;fmt&quot;
	&quot;reflect&quot;
	&quot;strings&quot;
)

type User struct {
	Name string
	Age  byte
}

func main() {
	flag.Parse()
	fmt.Println(String(flag.Args()))
	fmt.Println(String([]string{&quot;hello&quot;, &quot;world&quot;}))
	fmt.Println(String([]int{1, 2, 3, 4, 5, 6}))
	u1, u2 := User{Name: &quot;John&quot;, Age: 30},
		User{Name: &quot;Not John&quot;, 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 &quot;&quot;
		}
		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 &lt; l; i++ {
			sb.WriteString(&quot;,&quot;)
			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 &quot;flag&quot;

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

huangapple
  • 本文由 发表于 2012年10月21日 00:19:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/12990338.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定