无法将数据(类型为interface {})转换为字符串类型:需要类型断言

huangapple go评论79阅读模式
英文:

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

问题

我对Go语言还不太熟悉,最近在使用这个notify包进行实验。

起初,我的代码看起来是这样的:

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

我想在Hello World!后面添加换行符,但不是在上面的doit函数中,因为那样太简单了,而是在下面的handler函数中,像下面这样:

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

运行go run之后:

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

经过一番搜索,我找到了这个SO上的问题

然后我更新了我的代码:

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

这样做对吗?我的编译错误消失了,所以我猜这很好?这样做高效吗?你应该用不同的方法吗?

英文:

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(&quot;my_event&quot;, &quot;Hello World!&quot;)
    fmt.Fprint(w, &quot;+OK&quot;)
}

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(&quot;my_event&quot;, myEventChan)
    data := &lt;-myEventChan
    fmt.Fprint(w, data + &quot;\n&quot;)
}

After go run:

$ go run lp.go 
# command-line-arguments
./lp.go:15: invalid operation: data + &quot;\n&quot; (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(&quot;my_event&quot;, myEventChan)
    data := &lt;-myEventChan
    s:= data.(string) + &quot;\n&quot;
    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?

答案1

得分: 363

根据Go规范
> 对于一个接口类型的表达式x和一个类型T,主表达式x.(T)断言x不是nil,并且存储在x中的值是类型T。

"类型断言"允许您声明一个接口值包含某个具体类型,或者它的具体类型满足另一个接口。

在您的示例中,您断言数据(类型interface{})具有具体类型string。如果您错了,程序将在运行时发生恐慌。您不需要担心效率,检查只需要比较两个指针值。

如果您不确定它是否为字符串,可以使用两个返回值的语法进行测试。

str, ok := data.(string)

如果data不是字符串,ok将为false。然后,通常将这样的语句包装到if语句中,如下所示:

if str, ok := data.(string); ok {
    /* 对str进行操作 */
} else {
    /* 不是字符串 */
}
英文:

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 */
}

答案2

得分: 47

类型断言

这在golang中被称为类型断言,并且是一种常见的做法。

以下是来自 a tour of go 的解释:

> 类型断言提供了对接口值的底层具体值的访问。

t := i.(T)

> 这个语句断言 接口值 i 持有 具体类型 T,并将底层的 T 值赋给变量 t。

> 如果 i 不持有 T,则该语句将触发 panic。

> 要测试接口值是否持有特定类型,类型断言可以返回两个值:底层值和一个布尔值,报告断言是否成功。

t, ok := i.(T)

> 如果 i 持有 T,则 t 将是底层值,ok 将为 true。

> 如果不是,则 ok 将为 false,t 将是类型 T 的零值,并且不会触发 panic。

注意:i 应该是接口类型

注意事项

即使 i 是接口类型,[]i 不是接口类型。因此,为了将 []i 转换为其值类型,我们必须逐个进行转换:

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

性能

就性能而言,与直接访问实际值相比,它可能会更慢,如 这个stackoverflow答案 所示。

英文:

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.

答案3

得分: 36

str := fmt.Sprint(data)

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

答案4

得分: 7

根据 @ρяσѕρєя 的要求,可以在 https://golang.org/pkg/fmt/#Sprint 找到解释。相关的解释可以在 https://stackoverflow.com/a/44027953/12817546https://stackoverflow.com/a/42302709/12817546 找到。这是 @Yuanbo 的完整答案。

package main

import "fmt"

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

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 &quot;fmt&quot;

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

答案5

得分: 0

除了其他答案之外,我认为看一下“类型开关”是很好的:

package main

import "fmt"

func printType(i interface{}) {
	switch v := i.(type) {
	case int:
		fmt.Printf("类型为 %v 的值是 %v\n", i, v)
                 // 类型为 21 的值是 int
	case string:
		fmt.Printf("类型为 %v 的值是 %v\n", i, v)
                 // 类型为 hello 的值是 string
	default:
		fmt.Printf("类型为 %v 的值是 %v\n", i, v)
                 // 类型为 true 的值是 bool
	}
}

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

希望对你有所帮助。

更多信息:https://go.dev/tour/methods/16

英文:

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

package main

import &quot;fmt&quot;

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

func main() {
	printType(21)
	printType(&quot;hello&quot;)
	printType(true)
}

I hope it helps.

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

huangapple
  • 本文由 发表于 2013年1月12日 09:27:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/14289256.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定