swift: protocolとGenerics関数

次のコードは違法。

protocol Metric {
   var width: Double { get }
   var height: Double { get }
}

func area<T:Metric>(obj:T) -> Double 
{
   return obj.width * obj.height
}

class X: Metric {
   var width = 0.0
   var height = 0.0
}

let obj: Metric = X() // protocolで型制約
area(obj) // !Invalid!

この時「error: generic parameter ‘T’ cannot be bound to non-@objc protocol type ‘Metric’」という周りくどいエラーが出るので困る。

要はGenericsの関数はコンパイル時に実体を確定する必要があるため、型をprotocolで多態させている変数に適用することができない。swiftの場合、Array<Metric>などは普通に作れるので、うっかり使うとハマる。

次のようにすれば動的解決してくれて合法になる。パフォーマンスのオーバーヘッドは多少あるが、それならprotocolで多態するなという話だろう。

@objc protocol Metric {
   var width: Double { get }
   var height: Double { get }
}

なお、@objcがつくと自動的に: classのプロトコルになるので、struct等を対象にこの方法は使えない。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です