Edited at

Swiftの総称型がサポートしていないこと

More than 3 years have passed since last update.

Swiftの総称型がサポートしていないことを調べた。Swiftの総称(ジェネリクス)はとにかく制限が沢山あって現状だと使いづらいので、あまり考え過ぎないほうがよさそうだという気がしている。


総称型の型引数を使った型プロパティ定義

未サポート。"not yet supported" なのでいずれサポートされる可能性はありそう。とはいえ用途があるかどうかは難しい。

// Swift

class G<T> {
// class stored properties not yet supported in generic types
class var value: T? = nil
}

なおC++, C#ではできる。Javaではできない。

// C#

class G<T> {
public static T value;
}

class S: G<string> {
}
class I: G<int> {
}

// use
S.value = "foo";
I.value = 42;


総称クラスを継承して非総称クラスを定義する

未サポート。これは普通に使うのでないとつらい。

// Swift

class G<T> {

}

// ERROR: classes derived from generic classes must be generic
class N: G<Int> {

}

実際、Java, C#, C++いずれもサポートしている。

// C#

// 定義する
class G<T> {
public T value;
}
class S: G<string> {
}
class I: G<int> {
}

// 使う
var s = new S();
s.value = "foo";
var i = new I();
i.value = 42;


総称プロトコル型の変数を宣言する

なぜできないのか理解に苦しむ。だれか説明してくれ…。

// Swift

// in Swift.swift
protocol GeneratorType {
typealias Element
mutating func next() -> Element?
}

// Generator with Int を表現する方法がない
var g: GeneratorType; // ERROR: Protocol 'GeneratorType' can only be used as a generic constraint

以下のようにラッパークラスを使ってなんとか Set<Int>().generate()Array<Int>().generate() 両方を入れられる型を宣言できないかと思ったが、結局具象型が必要でラッパークラスを使わないケースと何ら変わりがない。

// Swift

// 失敗した例
struct GeneratorHolder<G: GeneratorType> {
var generator: G

init(_ generator: G) {
self.generator = generator
}
}

var a = [10, 20]

// GeneratorTypeをラップした型の変数を宣言できているように見えるが誤り
var g = GeneratorHolder(a.generate())
g.generator.next() // 10
g.generator.next() // 20
g.generator.next() // nil

// 以下はコンパイルエラー
// なぜなら、gの型はGeneratorHolder<IndexingGenerator<[Int]>>で
// 結局のところa.generate()の具体的な型で特殊化されてしまっているから
var s = Set(arrayLiteral: 10, 20)
g = GeneratorHolder(s.generate())


総称の明示的な特殊化

C++だとこれを使ったテンプレートメタプログラミングが大変発達していてる。Swiftではできないが、Javaでもできないので大きな問題は感じない。

// Swift

struct S<T> {}

struct S<Int> {} // ERROR

そもそもこれのできるC++は型引数にclassないしtypenameをつけるので構文上の区別ができるが、Swiftでは構文上の区別が付けられないのでいずれできるようになる可能性もなさそうだ。

// C++

template <typename T> struct S {};

template <> struct S<int> {}; // explicit specialization