121

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
  • 1
    I was messing with a simple example (go run test.go some test flags), and it seemed to work when changing flags.Args()... to just flag.Args() (output is [some test flags], followed by the newline; also seemed to work with registering actual flags). Won't pretend to understand why, and Stephen's answer is way more informative anyway :) Commented Oct 20, 2012 at 17:17

7 Answers 7

142

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.

Sign up to request clarification or add additional context in comments.

8 Comments

By the way, I found this link in golang-nuts: groups.google.com/d/topic/golang-nuts/Il-tO1xtAyE/discussion
Yes, each iteration requires O(1) time and the loop requires O(n) time. That is what I said. As for the function receiving a []string, it expects a interface{}. An interface{} has a different memory layout from a string so the fact that each element needs to be converted is the problem.
@karlrh: No, suppose the Println function modifies the slice, and sets some elements (it doesn't, but suppose it does). Then it can put any interface{} into the slice, which should only have strings. What you really want is something like the Java Generics wildcard Slice<? extends []interface{}>, but that doesn't exist in Go.
Append is magical. It is built-in and treated specially by the language. Other examples include new(), len(), and copy(). golang.org/ref/spec#Appending_and_copying_slices
Today I learned 'new' is a not a reserve keyword! Nor is make!
|
14
+50

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] $ 

1 Comment

If I wanted to omit the brackets, would the conversion still be unnecessary?
11

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

Comments

1

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.

Comments

0

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} 

Comments

0

Another option is to just iterate the slice:

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

Comments

-2

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

8 Comments

fmt.Println's signature hasn't changed in over 6 years (and that was just a package change for error). Even if it had, the spec clearly says ` variadic with a final parameter p of type ...T, then within f the type of p is equivalent to type []T.` so it wouldn't matter. fmt.Println(flags.Args()...) still does not work (you're missing the slice expansion), fmt.Println(flags.Args()) always worked.
How did you get this info that fmt.Println's signature hasn't changed in over 6 years? Did you checked source code?
The comment on the op suggest that using flag.Args() solely as argument to fmt.Println worked back that time. (It would be surprising if it did not at that time)
So? If there exist a comment, that means my answer should be down voted?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.