🌟
Swiftにおける冗長に見えるジェネリクス型パラメータの価値
はじめに
Swiftでジェネリクスを使っていると、こんな型定義を見かけることがあります:
struct Stored<Value, Storage: StorageProtocol<Value>> {
// ...
}
一見すると冗長で、Stored<Storage: StorageProtocol> で十分に思えますよね。でも実は、この「冗長さ」には重要な理由があります。
なぜ型パラメータを分けるのか?
1. 具体的な型での制約が書きやすい
最大の利点は、extensionで具体的な型制約を簡潔に書けることです:
// Value を分けている場合
extension Stored where Storage == InMemoryStorage<Value> {
func preloadCache() {
// InMemoryStorage 特有の最適化
}
}
extension Stored where Storage == FileStorage<Value> {
func compressBeforeSaving() {
// FileStorage 特有の処理
}
}
もし Stored<Storage> だけだったら、内部の型パラメータにアクセスする方法がなく、このような制約は書けません。
別のアプローチ:プロトコルを活用する方法
型パラメータを分けない場合、Storage種別ごとのプロトコルを定義する方法もあります:
protocol InMemoryStorageProtocol: StorageProtocol {
func preloadCache()
}
protocol FileStorageProtocol: StorageProtocol {
func compressBeforeSaving()
}
// 使用例
extension Stored where Storage: InMemoryStorageProtocol {
func optimizeMemory() {
storage.preloadCache()
}
}
このアプローチでは、Storageの種類に応じた振る舞いをプロトコルで表現します。
Discussion