web-dev-qa-db-ja.com

Goで型エイリアスにキャストする方法は?

この遊び場スニペットを参照してください。

関連コード:

type somethingFuncy func(int) bool

func funcy(i int) bool {
    return i%2 == 0
}

var a interface{} = funcy

func main() {

    _ = a.(func(int) bool)  // Works

    fmt.Println("Awesome -- apparently, literally specifying the func signature works.")

    _ = a.(somethingFuncy)  // Panics

    fmt.Println("Darn -- doesn't get here. But somethingFuncy is the same signature as func(int) bool.")
}

最初のキャストは、型を明示的に宣言することで機能します。しかし、2番目のキャストはパニックに陥ります。どうして?より長いfuncシグネチャにキャストするクリーンな方法はありますか?

32
Matt

tl; dr

型アサーション(使用する)の場合、実際の型のみが重要です。したがって、somethingFuncysomethingFuncyにのみ等しく、func(int) boolではありません。

説明

まず、これはキャストとは関係ありません。進行中のキャストはありません。 タイプアサーションタイプ変換 があります。

タイプアサーションを扱っており、タイプconversionsと同じ条件が当てはまると仮定しています。私はあなたの質問を読んでいる間に同じ間違いを犯しましたが、実際には行動に大きな違いがあります。

intと_type MyInt int_の2つのタイプがあるとします。これらは両方とも同じ基本タイプ(変換ルールの1つ)を共有するため、変換可能であるため、これは機能します( play ):

_var a int = 10
var b MyInt = MyInt(a)
_

ここで、aがタイプintではなく、タイプ_interface{}_であるとします( play ):

_var a interface{} = int(10)
var b MyInt = MyInt(a)
_

コンパイラは次のことを通知します。

(type interface {})をMyInt型に変換できません:型アサーションが必要です

そのため、今は変換ではなくアサーションを行っています。これを行う必要があります( play ):

_var a interface{} = int(10)
var b MyInt = a.(MyInt)
_

今、あなたの質問と同じ問題があります。このアサーションはこのパニックで失敗します:

パニック:インターフェース変換:インターフェースはmain.MyIntではなくint

この理由は、仕様の type assertionsセクション に記載されています。

インターフェイスタイプの式xおよびタイプTの場合、プライマリ式x.(T)は、xnilではなく、xに格納されている値がTタイプであることをアサートします。表記x.(T)は、タイプアサーションと呼ばれます。 より正確には、Tがインターフェース型ではない場合、x.(T)xの動的型がT型と同一であることをアサートします。

したがって、intMyIntと同一でなければなりません。 type identity のルールは(他のルールの中で)次のことを述べています:

タイプ名が同じTypeSpecに由来する場合、2つの名前付きタイプは同一です。

intMyIntの宣言は異なるため( TypeSpecs )等しくないため、アサーションは失敗します。 aintにアサートすると、アサーションが機能します。したがって、あなたがしていることは不可能です。

ボーナス:

実際のチェックは このコードでは で行われます。これは、両方のタイプが同じであるかどうかを期待どおりにチェックするだけです。

56
nemo

Nemoのすばらしい答えを完成させるために、インターフェースから直接ジャンプすることはできませんが(e.f.、interface{})特定の動的タイプ(例:int)から別のタイプ(例:type MyInt int)、次の2つの手順を実行できます。

  • あなたの変数の動的な型はあなたが期待するものであると断言してください。
  • そのアサーションの結果を選択したタイプに変換します。

基になる型はその名前が示すようにdynamicであるため、型の表明が成功したか失敗したかをテストすることをお勧めします。一方、型変換の正確性はコンパイラーによって強制されます。

プレイグラウンドスニペットを少し変更しました: http://play.golang.org/p/FZv06Zf7xi

7
FelixCQ

型エイリアスはあなたが望むものだと思います。提案は受け入れられ、Go 1.9で行われるはずです。すなわち。

TypeSpec = identifier [ "=" ] Type .

参照資料
https://github.com/golang/go/issues/181
https://github.com/golang/proposal/blob/master/design/18130-type-alias.md

1
Anssi