LoginSignup
1
1

More than 5 years have passed since last update.

【swift4.1】複数のProtocolを継承しているProtocolへのConditional Conformance

Last updated at Posted at 2018-05-17

何気なく書いていて知らなかったことに気がついたので忘れないようにメモします。

経緯

下記のようなprotocolに適合したValueを持つstructを定義します。

protocol Identifiable {
    associatedtype RawIdentifier: Codable = String
    var id: Identifier<Self> { get }
}

struct Identifier<Value: Identifiable> {
    let rawValue: Value.RawIdentifier
    init(rawValue: Value.RawIdentifier) {
        self.rawValue = rawValue
    }
}

これを文字列から初期化できるようにExpressibleByStringLiteralに適合しようとします。

extension Identifier: ExpressibleByStringLiteral
where Value.RawIdentifier == String {

    typealias StringLiteralType = String

    init(stringLiteral value: String) {
        rawValue = value
    }
}

そうすると下記のようなエラーが出ます。

error: test3.playground:9:1: error: type 'Identifier<Value>' does not conform to protocol 'ExpressibleByExtendedGraphemeClusterLiteral'
extension Identifier: ExpressibleByStringLiteral
^

※ExpressibleByStringLiteralはExpressibleByExtendedGraphemeClusterLiteralを継承し、ExpressibleByExtendedGraphemeClusterLiteralは
ExpressibleByUnicodeScalarLiteralを継承しています。

「あれConditional Conformanceは?」と思ったのですが、下記に記載がありました。
https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md#implied-conditional-conformances
https://forums.swift.org/t/conditional-conformance-with-protocol-inheritance/11262/11
https://forums.swift.org/t/amending-se-0143-conditional-conformances-dont-imply-other-conformances/11017/2

いまいち全体の要領がつかめなかったのですが、議論の中で出てきている

the heart of the issue is 
that you may not have more than one extension 
that conforms a type to a particular protocol. 
Otherwise it’s ambiguous 
which versions of the protocol methods should be called 
when you invoke protocol methods on that type.

から、
「適用できるメソッドが複数ある場合、コンパイラーが何を選べばいいのわからない」ということのようです。(合っているのでしょうか?)

結論

下記のようにProtocolを明示して、typealiasを追加することでエラーは解消されました。

extension Identifier: ExpressibleByStringLiteral, ExpressibleByExtendedGraphemeClusterLiteral, ExpressibleByUnicodeScalarLiteral
where Value.RawIdentifier == String {

    typealias ExtendedGraphemeClusterLiteralType = String
    typealias UnicodeScalarLiteralType = String
    typealias StringLiteralType = String

    init(stringLiteral value: String) {
        rawValue = value
    }
}

一つ勉強になりました。

補足

上記の実装は
https://www.swiftbysundell.com/posts/type-safe-identifiers-in-swift
の内容を色々触っていたときに気がつき、著者の@johnsundellさんにもTwitterで聞いてみたところ必要だとの回答をもらいました。
加えて、元々ExtendedGraphemeClusterLiteralTypeとUnicodeScalarLiteralTypeのinitメソッドも追加していたのですが、typealiasだけで良いということも教えてもらいました:smiley:

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1