180

I'm using docopt to parse command-line arguments. This works, and it results in a map, such as

map[<host>:www.google.de <port>:80 --help:false --version:false] 

Now I would like to concatenate the host and the port value to a string with a colon in-between the two values. Basically, something such as:

host := arguments["<host>"] + ":" + arguments["<port>"] 

Unfortunately, this doesn't work, as I get the error message:

invalid operation: arguments[""] + ":" (mismatched types interface {} and string)

So obviously I need to convert the value that I get from the map (which is just interface{}, so it can be anything) to a string. Now my question is, how do I do that?

2
  • 6
    golang.org/ref/spec#Type_assertions Commented Nov 25, 2014 at 22:00
  • 1
    fmt.Sprint(data) // where data is interface{} Commented Jan 12, 2022 at 22:48

4 Answers 4

252

You need to add type assertion .(string). It is necessary because the map is of type map[string]interface{}:

host := arguments["<host>"].(string) + ":" + arguments["<port>"].(string) 

Latest version of Docopt returns Opts object that has methods for conversion:

host, err := arguments.String("<host>") port, err := arguments.String("<port>") host_port := host + ":" + port 
Sign up to request clarification or add additional context in comments.

2 Comments

Would like to add that this is called Type Assertion - golang.org/ref/spec#Type_assertions. I once had trouble figuring out this exact terminology.
It will cause a panic if arguments["<host>"] is nil
117

You don't need to use a type assertion, instead just use the %v format specifier with Sprintf:

hostAndPort := fmt.Sprintf("%v:%v", arguments["<host>"], arguments["<port>"]) 

2 Comments

This was the only thing I could get to work for me when trying to type cast interface{}'s with as strings.
For me, this didn't work. This ended up printing the byte ASCII charts instead of the string, like 201 become "[50,48,49]", so I ended up using fmt.Sprintf("%s", theThing) instead...
29

To expand on what Peter said: Since you are looking to go from interface{} to string, type assertion will lead to headaches since you need to account for multiple incoming types. You'll have to assert each type possible and verify it is that type before using it.

Using fmt.Sprintf (https://golang.org/pkg/fmt/#Sprintf) automatically handles the interface conversion. Since you know your desired output type is always a string, Sprintf will handle whatever type is behind the interface without a bunch of extra code on your behalf.

Comments

0

You could also use text/template:

package main import ( "text/template" "strings" ) func format(s string, v interface{}) string { t, b := new(template.Template), new(strings.Builder) template.Must(t.Parse(s)).Execute(b, v) return b.String() } func main() { m := map[string]interface{}{"<host>": "www.google.de", "<port>": "80"} s := format(`{{index . "<host>"}}:{{index . "<port>"}}`, m) println(s == "www.google.de:80") } 

https://pkg.go.dev/text/template

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.