Reputation: 2999
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?
Upvotes: 120
Views: 72940
Reputation: 1
Another option is to just iterate the slice:
package main
import "flag"
func main() {
flag.Parse()
for _, each := range flag.Args() {
println(each)
}
}
Upvotes: 0
Reputation: 104
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}
Upvotes: 0
Reputation: 4421
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.
Upvotes: 1
Reputation: 166569
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()...) }
import "flag"
func Args() []string
Args
returns the non-flag command-line arguments.
import "fmt"
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]
$
Upvotes: 14
Reputation: 13804
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())
}
Upvotes: -2
Reputation: 16420
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)
}
Upvotes: 11
Reputation: 53418
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.
Upvotes: 141