🌟

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