cannot convert data (type interface {}) to type string: need type assertion

GoType Mismatch

Go Problem Overview


I am pretty new to go and I was playing with this notify package.

At first I had code that looked like this:

func doit(w http.ResponseWriter, r *http.Request) {
    notify.Post("my_event", "Hello World!")
    fmt.Fprint(w, "+OK")
}

I wanted to append newline to Hello World! but not in the function doit above, because that would be pretty trivial, but in the handler afterwards like this below:

func handler(w http.ResponseWriter, r *http.Request) {
    myEventChan := make(chan interface{})
    notify.Start("my_event", myEventChan)
    data := <-myEventChan
    fmt.Fprint(w, data + "\n")
}

After go run:

$ go run lp.go 
# command-line-arguments
./lp.go:15: invalid operation: data + "\n" (mismatched types interface {} and string)

After a little bit of Googling I found this question on SO.

Then I updated my code to:

func handler(w http.ResponseWriter, r *http.Request) {
    myEventChan := make(chan interface{})
    notify.Start("my_event", myEventChan)
    data := <-myEventChan
    s:= data.(string) + "\n"
    fmt.Fprint(w, s)
}

Is this what I was supposed to do? My compiler errors are gone so I guess that's pretty good? Is this efficient? Should you do it differently?

Go Solutions


Solution 1 - Go

According to the Go specification: > For an expression x of interface type and a type T, the primary expression x.(T) asserts that x is not nil and that the value stored in x is of type T.

A "type assertion" allows you to declare an interface value contains a certain concrete type or that its concrete type satisfies another interface.

In your example, you were asserting data (type interface{}) has the concrete type string. If you are wrong, the program will panic at runtime. You do not need to worry about efficiency, checking just requires comparing two pointer values.

If you were unsure if it was a string or not, you could test using the two return syntax.

str, ok := data.(string)

If data is not a string, ok will be false. It is then common to wrap such a statement into an if statement like so:

if str, ok := data.(string); ok {
    /* act on str */
} else {
    /* not string */
}

Solution 2 - Go

Type Assertion

This is known as type assertion in golang, and it is a common practice.

Here is the explanation from a tour of go:

> A type assertion provides access to an interface value's underlying concrete value.

t := i.(T)

> This statement asserts that the interface value i holds the concrete type T and assigns the underlying T value to the variable t.

> If i does not hold a T, the statement will trigger a panic.

> To test whether an interface value holds a specific type, a type assertion can return two values: the underlying value and a boolean value that reports whether the assertion succeeded.

t, ok := i.(T)

> If i holds a T, then t will be the underlying value and ok will be true.

> If not, ok will be false and t will be the zero value of type T, and no panic occurs.

NOTE: value i should be interface type.

Pitfalls

Even if i is an interface type, []i is not interface type. As a result, in order to convert []i to its value type, we have to do it individually:

// var items []i
for _, item := range items {
    value, ok := item.(T)
    dosomethingWith(value)
}

Performance

As for performance, it can be slower than direct access to the actual value as show in this stackoverflow answer.

Solution 3 - Go

//an easy way:
str := fmt.Sprint(data)

Solution 4 - Go

As asked for by @ρяσѕρєя an explanation can be found at https://golang.org/pkg/fmt/#Sprint. Related explanations can be found at https://stackoverflow.com/a/44027953/12817546 and at https://stackoverflow.com/a/42302709/12817546. Here is @Yuanbo's answer in full.

package main

import "fmt"

func main() {
	var data interface{} = 2
	str := fmt.Sprint(data)
	fmt.Println(str)
}

Solution 5 - Go

In addition to other answers, I think it's good to have a look at "type switch":

package main

import "fmt"

func printType(i interface{}) {
	switch v := i.(type) {
	case int:
		fmt.Printf("type of %v is %v\n", i, v)
                 // type of 21 is int
	case string:
		fmt.Printf("type of %v is %v\n", i, v)
                 // type of hello is string
	default:
		fmt.Printf("type of %v is %v\n", i, v)
                 // type of true is bool
	}
}

func main() {
	printType(21)
	printType("hello")
	printType(true)
}

I hope it helps.

More information: https://go.dev/tour/methods/16

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
QuestionAlfredView Question on Stackoverflow
Solution 1 - GoStephen WeinbergView Answer on Stackoverflow
Solution 2 - GocizixsView Answer on Stackoverflow
Solution 3 - GoYuanboView Answer on Stackoverflow
Solution 4 - Gouser12817546View Answer on Stackoverflow
Solution 5 - GoArshamView Answer on Stackoverflow