56895


# はじめに

[Call can throw, but it is not marked with 'try' and the error is not
handled](https://ja.stackoverflow.com/questions/56873/call-can-throw-but-it-
is-not-marked-with-try-and-the-error-is-not-handled)  
の質問に対して、自分自身で下記の回答をしました。  
<https://ja.stackoverflow.com/a/56880/9008>

# オープンソース

自分自身で回答しておきながら、よくわからない部分があるのですが、  
オープンソースを見てみるとよくあるテクニック（イディオム）のように思えてきました。

## SwiftyUserDefaults（引用）

<https://github.com/radex/SwiftyUserDefaults/blob/abcfe895e3119b051918fa19b8d8e009cdc44cce/Sources/OptionalType.swift>

[code]

    public protocol OptionalType {
        associatedtype Wrapped
        var wrapped: Wrapped? { get }
    }
    
    extension Optional: OptionalType {
    
        public var wrapped: Wrapped? {
            return self
        }
    }
    
[/code]

## Realm（引用）

<https://github.com/realm/realm-
cocoa/blob/930eb761c418cdd9babf2e599fb79ff22e371ffb/RealmSwift/RealmCollection.swift>

[code]

    /// :nodoc:
    public protocol OptionalProtocol {
        associatedtype Wrapped
        /// :nodoc:
        // swiftlint:disable:next identifier_name
        func _rlmInferWrappedType() -> Wrapped
    }
    
    extension Optional: OptionalProtocol {
        /// :nodoc:
        // swiftlint:disable:next identifier_name
        public func _rlmInferWrappedType() -> Wrapped { return self! }
    }
    
[/code]

# 質問

この `OptionalType` や `OptionalProtocol` はどういったテクニックなのでしょうか？

特に  
`associatedtype Wrapped`  
と  
Swift自体の  
`public enum Optional<Wrapped> : ExpressibleByNilLiteral`  
の`<Wrapped>` の関係性について教えて下さい。


CC BY-SA 4.0
2019-07-25T07:13:26.887
56882
2019-07-28T01:13:03.610
2019-07-28T01:13:03.610
9008
9008
question
swift optional ジェネリクス
OptionalType もしくは OptionalProtocol はどういったテクニックなのでしょうか？


**_この`OptionalType` や `OptionalProtocol` はどういったテクニックなのでしょうか？_**

Swiftのジェネリックに対する制限を回避するために **`Optional`型だけが適合するプロトコル**として宣言されています。

(`Optional`以外で`OptionalType` や
`OptionalProtocol`に適合する型を定義することもできますが、例として挙げられた両者ともそんなことは少しも考慮していません。)

別質問の方で話題にされたようなメソッド、要素型が`Optional`の時にだけ働くような拡張メソッドを定義したい場合、現在のSwiftでは、それを自然に表現することが出来ません。

[code]

    extension Array where Element == Optional {
        //...
    }
    
[/code]

`Optional`のようなジェネリック型を使用する場合、型パラメータを明示しないとエラーになります。

[code]

    extension Array where Element == Optional<T> {
        //...
    }
    
[/code]

しかし、上のように書くと、未定義型`T`を使用していると言って怒られます。

[code]

    extension<T> Array where Element == Optional<T> {
        //...
    }
    
[/code]

しかし、Swiftでは、extensionに新たなジェネリックパラメータを導入することは出来ず、上のような書き方はまともに構文解析さえしてくれず、意味不明のエラーになります。

そこで`Element`が`Optional`型であると言う制約を表現するために、`OptionalType`なんてプロトコルを導入し、`Optional`型だけをそのプロトコルに適合させておきます。

つまり、  
「ある型が`OptionalType`に適合している」⇔「その型は`Optional`型である」  
と言う状態にしてしまうわけです。

その状態では、

[code]

    extension Array where Element: OptionalType {
        //...
    }
    
[/code]

なんて宣言してやれば、`Element`が`Optional`型の時にだけ有効なextensionが宣言できると言うわけです。

特に昔のSwiftでは、ジェネリック型に`==`での制約をつけることができなかったこともあり、よく使われたテクニックです。

* * *

現在では`==`を使った制約も使えますし、メソッド単位であればextension中で新たなジェネリックパラメータを導入することも出来るので、masakih
さんの回答にあるようなコードの書き方の方が自然でしょう。

[code]

    extension Sequence {
        func removeNils<T>() -> [T]
            where Element == Optional<T>
        {
            return self.compactMap { $0 }
        }
    
        func mapNotNil<R, T>(_ transform: (T) throws -> R) rethrows -> [R]
            where Element == Optional<T>
        {
            return try self.compactMap{try $0.map{try transform($0)}}
        }
    }
    
[/code]

すべてのメソッドに`where Element ==
Optional<T>`なんて制約を書くより、`Optional`型であることを示すためだけのプロトコルを定義する方がまし…なんて思う人もいるかもしれませんが。

* * *

**_`associatedtype Wrapped`  
と  
Swift自体の  
`public enum Optional<Wrapped> : ExpressibleByNilLiteral`  
の`<Wrapped>` の関係性について_**

`OptionalType.Wrapped`は、プロトコル`OptionalType`の関連型(associatedtype)、`Optional.Wrapped`はジェネリック型`Optional`の型パラメータであるわけですが、`Optional`型をプロトコル`OptionalType`に適合させることにより、両者は同じ型であるものと扱われるようになります。

[code]

    protocol MyProtocol {
        associatedtype MyAssocType
    
        var assocValue: MyAssocType? {get}
    }
    extension MyProtocol {
        func showMyAssocType() {
            print(MyAssocType.self)
        }
    }
    
    class MyClass<T> {
        var assocValue: T? = nil
    
        func showT() {
            print(T.self)
        }
    }
    extension MyClass: MyProtocol {}
    let myObj = MyClass<Int>()
    myObj.showT() //->Int
    myObj.showMyAssocType() //->Int
    
[/code]

* * *

**追記** コメントでいただいた内容について。

少し説明を端折りすぎてしまったようですね。

**_`associatedtype` の名前はなんでもよいと思う_**

その通りです。「`associatedtype` の名前」に大きな意味はありません。

[code]

    protocol OptionalType {
        associatedtype Hoge
    }
    extension Optional: OptionalType {
    }
    
[/code]

**_で、`Type 'Optional<Wrapped>' does not conform to protocol 'OptionalType'`
というエラーになります。_**

これまたその通りですね。ただし、このエラーは「`associatedtype`
の名前」が異なっているから発生しているのではなく、「`Hoge`がどのような型になるか決定できない」ために、起こっているものです。

例えば、次のように書いてやると、エラーは出なくなります。

[code]

    protocol OptionalType {
        associatedtype Hoge
    
        var optional: Hoge? { get }
    }
    extension Optional: OptionalType {
        var optional: Wrapped? {
            return self
        }
    }
    
[/code]

この場合、Swiftはプロトコル`OptionalType`の実装には`var optional:
Hoge?`というプロパティ(最低getterだけでOK)が必要だということを知っている状態で、`Optional`型を見るわけですが、すぐに`var
optional: Wrapped? {...}`と言うプロパティが宣言されているのが見つかります。

`var optional: Hoge?`  
↑↓  
`var optional: Wrapped?`

この結果、Swiftの型推論が働いて、`OptionalType.Hoge`は`Optional.Wrapped`と同じ型である、と言うことになるわけです。

このように、「 **Swiftが型推論できるような実装を含めてやることで、異なる型名でも両者が同じものを表すとSwiftにわからせることが出来ます** 。」

説明がほとんど無かった点については大変失礼しましたが、先の例でも`MyProtocol.MyAssocType`と`MyClass.T`の「名前」は全く違っているのに、なぜ同じ型であるものと扱われるのか、はお分りいただけましたでしょうか。

まだまだ言葉足らずかもしれませんが、わかりにくいところがあれば、またコメントしていただけると助かります。


CC BY-SA 4.0
2019-07-25T18:37:28.907
56895
2019-07-26T10:52:47.890
2019-07-26T10:52:47.890
13972
13972
56882
answer