Edited at

SwiftのassociatedtypeをSelfに束縛する

More than 1 year has passed since last update.


※ここで紹介するWorkaroundはSwift3.1で使えなくなりました。

セグフォでコンパイル出来ない状況です。

https://bugs.swift.org/browse/SR-4434

↑これと混同していました。

コンパイルは通りますが、親クラスに束縛されてしまって目的は達成できません。


 以下本文

Swift2.2になって、Protocol周りのバグが幾つか修正されて、ProtocolとClassを組み合わせて使うシチュエーションが現実的になってきました。

ということで、Swift2.2から可能になったテクニックを備忘録も兼ねて紹介します。


本題

さて、Swift2.1から、Classにおいて、返り値と引数の型としてSelfを使うことが出来るようになったのですが、associatedtypeをSelfで束縛することは、Swift2.2ではまだ出来ないように見えます。

protocol A {

associatedtype X
}

class B: A {
typealias X = Self // 'Self' is only available in a protocol or as the result of a method in a class; did you mean 'B'?

@noreturn
static func defaultInstance() -> Self { // いける
fatalError()
}
}

いや、XをSelfで束縛したいんですよ…

こういう時は、protocol extensionを使うことでSelfで束縛しなおすことが出来ます。

class B: A {

typealias X = B
}

extension A where Self: B {
typealias X = Self
}

class C: B {
}
C.X.self // C.Type

実はこのテクニック、Swift2.1ではクラスに束縛されたassociatedtypeがコンパイラに認識されなくなるバグがあったため使えませんでした。

// 以下Swift2.1

extension A where Self: B {
typealias X = Self
}

B.X.self //'X' is ambiguous for type lookup in this context
// ↑????!?!?!?!?wwww

Swiftのアップデートで出来ることが増えました。幸せですね。

このテクニックは拙作のライブラリで使用しています。

https://github.com/tarunon/Barrel


追記(2016/03/24)

以下の方法で型を破壊することが出来ました。

protocol A {

associatedtype X
}

extension A where Self: AnyObject {
typealias X = Self
}

protocol B: A {
associatedtype Y: A
associatedtype X = Y.X // Ambiguous type name 'X' in 'Self.Y'
}

typealias, associatedtypeにおける宣言のみ再現します。

これについては、extensionによる束縛を、別のライブラリに退避させるworkaroundが有効です。