web-dev-qa-db-ja.com

データ(タイプインターフェイス{})をタイプ文字列に変換できません:タイプアサーションが必要です

私はかなり新しいので、この 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)
}

これは私がやるべきことでしたか?コンパイラエラーがなくなったので、それはかなり良いと思いますか?これは効率的ですか?別のやり方でやるべきですか?

149
Alfred

Go仕様 に従って:

インターフェイスタイプとタイプTの式xの場合、プライマリ式x。(T)は、xがnilではなく、xに格納されている値がタイプTであることをアサートします。

「タイプアサーション」を使用すると、インターフェイス値に特定の具象タイプが含まれていること、または具象タイプが別のインターフェースを満たすことを宣言できます。

あなたの例では、データ(type interface {})に具象型文字列があると断言していました。間違っていると、実行時にプログラムがパニックになります。効率について心配する必要はありません。チェックするには、2つのポインタ値を比較するだけです。

文字列かどうかわからない場合は、2つの戻り構文を使用してテストできます。

str, ok := data.(string)

データが文字列でない場合、okはfalseになります。そのようなステートメントを次のようなifステートメントにラップするのが一般的です。

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

タイプアサーション

これはgolangではtype assertionとして知られており、一般的な方法です。

goのツアー の説明を次に示します。

型アサーションは、インターフェイス値の基礎となる具体的な値へのアクセスを提供します。

t := i.(T)

このステートメントは、インターフェイス値iがconcrete typeTを保持していることを表明します。基になるT値を変数tに割り当てます。

IがTを保持していない場合、ステートメントはパニックを引き起こします。

インターフェイス値が特定の型を保持しているかどうかをテストするために、型アサーションは2つの値を返すことができます。基礎となる値と、アサーションが成功したかどうかを報告するブール値です。

t, ok := i.(T)

IがTを保持している場合、tは基になる値になり、okはtrueになります。

そうでない場合、okはfalseになり、tはT型のゼロ値になり、パニックは発生しません。

注:value iはインターフェイスタイプである必要があります。

落とし穴

iがインターフェース型であっても、[]iはインターフェース型ではありません。その結果、[]iをその値型に変換するには、個別に行う必要があります。

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

性能

パフォーマンスに関しては、 this stackoverflow answer に示すように、実際の値への直接アクセスよりも遅くなる可能性があります。

16
cizixs
//an easy way:
str := fmt.Sprint(data)
9
Yuanbo