Cannot convert []string to []interface {}

Type ConversionGo

Type Conversion Problem Overview


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?

Type Conversion Solutions


Solution 1 - Type Conversion

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.

Solution 2 - Type Conversion

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

Solution 3 - Type Conversion

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

Solution 4 - Type Conversion

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.

Solution 5 - Type Conversion

Another option is to just iterate the slice:

package main
import "flag"

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

Solution 6 - Type Conversion

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}

Solution 7 - Type Conversion

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncruizhView Question on Stackoverflow
Solution 1 - Type ConversionStephen WeinbergView Answer on Stackoverflow
Solution 2 - Type ConversionpeterSOView Answer on Stackoverflow
Solution 3 - Type ConversionZippoView Answer on Stackoverflow
Solution 4 - Type ConversionjrefiorView Answer on Stackoverflow
Solution 5 - Type ConversionZomboView Answer on Stackoverflow
Solution 6 - Type ConversionJannah QuetzalView Answer on Stackoverflow
Solution 7 - Type ConversionShahriarView Answer on Stackoverflow