By cruizh


2012-10-20 16:19:25 8 Comments

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?

6 comments

@Juan Torres 2019-03-05 19:06:08

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}

@jrefior 2018-02-04 09:26:25

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.

@peterSO 2018-01-31 05:27:32

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

@cruizh 2018-02-13 09:22:25

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

@aerokite 2018-01-30 18:02:01

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

@Marc 2018-01-30 18:31:59

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.

@aerokite 2018-01-30 18:36:36

How did you get this info that fmt.Println's signature hasn't changed in over 6 years? Did you checked source code?

@Marc 2018-01-30 18:38:15

@leaf bebop 2018-01-31 02:22:20

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)

@aerokite 2018-01-31 02:24:46

So? If there exist a comment, that means my answer should be down voted?

@aerokite 2018-01-31 02:26:19

@leafbebop where did you get this rules?

@leaf bebop 2018-01-31 02:29:12

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.

@aerokite 2018-01-31 02:31:01

@leafbebop I respect your personal right.

@leaf bebop 2018-01-31 02:43:56

And I think your edit to the question makes the question less general and more confusing. Is there really anything that requires revision?

@Moshe Revah 2015-12-19 16:06:35

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

@Stephen Weinberg 2012-10-20 16:42:51

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.

@cruizh 2012-10-20 17:06:55

If each iteration requires O(1) time, wouldn't the whole loop need O(n) time?

@cruizh 2012-10-20 17:14:16

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 2012-10-20 17:51:01

By the way, I found this link in golang-nuts: groups.google.com/d/topic/golang-nuts/Il-tO1xtAyE/discussion

@Stephen Weinberg 2012-10-20 17:52:15

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.

@newacct 2012-10-21 02:05:51

@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.

@zhaozhi 2014-01-23 08:52:14

but why append can accept any slice?

@Stephen Weinberg 2014-01-23 17:52:24

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

@A-letubby 2014-03-31 08:24:07

@icza 2015-06-02 06:57:01

Explicit conversion to interface{} is unnecessary, instead of new[i] = interface{}(v) you can simply write new[i] = v.

@Sridhar 2015-08-13 12:00:14

Today I learned 'new' is a not a reserve keyword! Nor is make!

Related Questions

Sponsored Content

31 Answered Questions

[SOLVED] How do I check if a string is a number (float)?

23 Answered Questions

[SOLVED] How do I parse a string to a float or int?

5 Answered Questions

[SOLVED] Type converting slices of interfaces

32 Answered Questions

[SOLVED] Converting a string to a date in JavaScript

27 Answered Questions

[SOLVED] Easiest way to convert int to string in C++

43 Answered Questions

[SOLVED] How do I convert a String to an int in Java?

13 Answered Questions

[SOLVED] How to convert UTF-8 byte[] to string?

2 Answered Questions

[SOLVED] How to define a function type which accepts any number of arguments in Go?

  • 2011-05-18 15:48:57
  • ceving
  • 1457 View
  • 7 Score
  • 2 Answer
  • Tags:   go

3 Answered Questions

[SOLVED] How do I convert a String to an InputStream in Java?

11 Answered Questions

[SOLVED] How to convert a char to a String?

Sponsored Content