117

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?

Shahriar
  • 13,460
  • 8
  • 78
  • 95
cruizh
  • 2,839
  • 3
  • 18
  • 20
  • 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 :) – RocketDonkey Oct 20 '12 at 17:17

7 Answers7

139

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.

Stephen Weinberg
  • 51,320
  • 14
  • 134
  • 113
  • If each iteration requires O(1) time, wouldn't the whole loop need O(n) time? – cruizh Oct 20 '12 at 17:06
  • If every element in the slice satisfies `interface{}`, then a function receiving `[]string` instead should have no problem, since every element inside satisfies the interface, is it? – cruizh Oct 20 '12 at 17:14
  • 2
    By the way, I found this link in golang-nuts: https://groups.google.com/d/topic/golang-nuts/Il-tO1xtAyE/discussion – cruizh Oct 20 '12 at 17:51
  • 2
    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. – Stephen Weinberg Oct 20 '12 at 17:52
  • 2
    @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 `string`s. What you really want is something like the Java Generics wildcard `Slice extends []interface{}>`, but that doesn't exist in Go. – newacct Oct 21 '12 at 02:05
  • 4
    Append is magical. It is built-in and treated specially by the language. Other examples include `new()`, `len()`, and `copy()`. http://golang.org/ref/spec#Appending_and_copying_slices – Stephen Weinberg Jan 23 '14 at 17:52
  • Explicit conversion to `interface{}` is unnecessary, instead of `new[i] = interface{}(v)` you can simply write `new[i] = v`. – icza Jun 02 '15 at 06:57
  • 1
    Today I learned 'new' is a not a reserve keyword! Nor is make! – Sridhar Aug 13 '15 at 12:00
14

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]
$ 
peterSO
  • 158,998
  • 31
  • 281
  • 276
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)
}
Zippo
  • 15,850
  • 10
  • 60
  • 58
0

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.

jrefior
  • 4,092
  • 1
  • 19
  • 29
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}
0

Another option is to just iterate the slice:

package main
import "flag"

func main() {
   flag.Parse()
   for _, each := range flag.Args() {
      println(each)
   }
}
Zombo
  • 1
  • 62
  • 391
  • 407
-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())
}
Shahriar
  • 13,460
  • 8
  • 78
  • 95
  • 2
    `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](https://golang.org/ref/spec#Passing_arguments_to_..._parameters) 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. – Marc Jan 30 '18 at 18:31
  • How did you get this info that fmt.Println's signature hasn't changed in over 6 years? Did you checked source code? – Shahriar Jan 30 '18 at 18:36
  • 1
    Yup: https://github.com/golang/go/blame/release-branch.go1.10/src/fmt/print.go#L260 (on the 1.10 branch) – Marc Jan 30 '18 at 18:38
  • 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) – leaf bebop Jan 31 '18 at 02:22
  • So? If there exist a comment, that means my answer should be down voted? – Shahriar Jan 31 '18 at 02:24
  • @leafbebop where did you get this rules? – Shahriar Jan 31 '18 at 02:26
  • I vote the answer down because it is not helpful. It does not answer the question. My comment served to tell you that there is nothing new. And I think to downvote is a very personal right of a user that does not need to be justified. – leaf bebop Jan 31 '18 at 02:29
  • And I think your edit to the question makes the question less general and more confusing. Is there really anything that requires revision? – leaf bebop Jan 31 '18 at 02:43