Соответствие CodingKeys приводит к ошибке компиляции - PullRequest
0 голосов
/ 29 марта 2019

У меня есть структура DynamicKey, которая соответствует CodingKey .. Тогда я решил расширить уже существующую функциональность KeyedEncodingContainer функцией кодирования [String: Any] ..

Так что теперьЯ дохожу до части соответствия в моем Struct Foo, но я получаю ошибку компилятора ..

Любые идеи, почему компилятор говорит, что Foo.CodingKeys не соответствует CodingKeys, когда он наследуется от DynamicKey, чтоимеет соответствие?

Не рабочий код:

struct DynamicKey: CodingKey, Equatable, ExpressibleByStringLiteral {
    var stringValue: String
    var intValue: Int? { return nil }

    init?(stringValue: String) {
        self.stringValue = stringValue
    }

    init?(intValue: Int) {
        return nil
    }

    //MARK:- Equatable Methods
    public static func == (lhs: DynamicKey, rhs: DynamicKey) -> Bool {
        return lhs.stringValue == rhs.stringValue
    }

    //MARK:- ExpressibleByStringLiteral Methods
    public init(stringLiteral value: String) {
        self.stringValue = value
    }

    public init(unicodeScalarLiteral value: String) {
        self.init(stringLiteral: value)
    }

    public init(extendedGraphemeClusterLiteral value: String) {
        self.init(stringLiteral: value)
    }
}

extension KeyedEncodingContainer where Key: CodingKey /*where Key == DynamicKey*/ {
    mutating func encodeDynamicValues(_ value: [String: Any], forKey key: Key) throws {
        //Other code here..
    }
}

struct Foo: Encodable {
    var arr: [String: Any]

    public func encode(to encoder: Encoder) throws {

        //Compiler Error: Instance method 'container(keyedBy:)' requires that 'Foo.CodingKeys' conform to 'CodingKey'
        //However, Foo.CodingKeys conforms to `CodingKey` because `DynamicKey` implements the protocol..
        var container = encoder.container(keyedBy: CodingKeys.self)



        try container.encodeDynamicValues(arr, forKey: .arr)
    }

    enum CodingKeys: DynamicKey {
        case arr
    }
}

Однако, если я изменю DynamicKey на класс, а затем использую оператор &, чтобы привести перечисление в соответствие, компиляторошибка исчезает (без &, она выдаст такую ​​же ошибку) .. Почему?

Рабочий код:

final class DynamicKey: CodingKey { //I don't need the equatable and expressible when it's a class so ignore that part.. adding it doesn't change anything..
    var stringValue: String
    var intValue: Int? { return nil }

    init?(stringValue: String) {
        self.stringValue = stringValue
    }

    init?(intValue: Int) {
        return nil
    }
}

extension KeyedEncodingContainer where Key: CodingKey /*where Key == DynamicKey*/ {
    mutating func encodeDynamicValues(_ value: Any, forKey key: Key) throws {
        //Other Code Here..
    }
}

struct Foo: Encodable {
    var arr: [String: Any]

    public func encode(to encoder: Encoder) throws {
        //CodingKeys now conforms to `CodingKey` because I made `DynamicKey` a class and used the `&` `CodingKey`
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encodeDynamicValues(arr, forKey: .arr)
    }

    enum CodingKeys: DynamicKey & CodingKey {
        case arr
    }
}

1 Ответ

0 голосов
/ 29 марта 2019

Ваш первый пример

Проблема в первом примере - это просто ваше понимание этой строки:

enum CodingKeys: DynamicKey

Вы говорите:

Есть идеи, почему компилятор говорит, что Foo.CodingKeys не соответствует CodingKeys, когда он наследует от DynamicKey, который имеет соответствие?

Но Foo.CodingKeys не «наследуется от DynamicKey». Ничто не может «наследовать» от структуры. Обозначения здесь не имеют ничего общего с наследованием. То, что вы сделали, это перечисление с необработанным значением типа DynamicKey. (Обычно вы не сможете этого сделать - необработанный тип значения должен быть «выражаемым литералом строки, целого числа или числа с плавающей точкой», но вы можете обойти это, сделав DynamicKey Equatable и ExpressibleByStringLiteral.)

Это тот же синтаксис, как если бы вы сказали:

enum MyEnum : Int {
    case myCase // zero
}

(Вы также запутали себя, используя магическое имя CodingKeys для своего перечисления. Но, вероятно, это не совсем так.)


Ваш второй пример

Тогда во втором примере, где DynamicKey является классом, вы запутались в совершенно противоположном направлении. Здесь вы сделали что-то совершенно другое:

enum CodingKeys: DynamicKey & CodingKey {

Здесь ваше перечисление не имеет какой-либо необработанный тип значения; материал после двоеточия объявляет ограничение протокола. Но здесь вы обманули себя еще одним способом, потому что на самом деле часть DynamicKey не имеет значения; код скомпилировался бы так же хорошо, если бы вы просто сказали

enum CodingKeys: CodingKey {

Все, что на самом деле происходит в этом примере, это то, что вы просили об автоматическом синтезе соответствия CodingKey, и вы его получили.

...