Как избежать бесконечного l oop при согласовании с протоколом с тем же именем переменной в Swift? - PullRequest
0 голосов
/ 30 мая 2020

Учитывая следующий протокол в FooModule:

protocol Id {
    var id: String { get }
}

protocol Foo {
    var id: Id { get }
}

... и некоторый тип из ThirdPartyModule, который ему не соответствует, но который уже имеет свойство id:

struct 3rdPartyId {
   var id: String
}

struct 3rdPartyElement {
    var id: 3rdPartyId
}

Теперь я хочу сделать:

extension 3rdPartyId: MyModule.Id {
    var id: String { return self.id }
}

extension 3rdPartyElement: MyModule.Element {
    var id: MyModule.Id { return self.id }
}

Однако это приводит к бесконечному l oop.

Как я могу расширить 3rdPartyElement для соответствия протоколу MyModule.Element без бесконечного l oop ...?

Я также пробовал просто объявить это так:

extension 3rdPartyElement: MyModule.Element {}

... потому что уже существует свойство id типа, которое должно удовлетворять протоколу. Однако это тоже не работает, я получаю сообщение об ошибке компилятора, предлагающее добавить средство получения свойства, которое, конечно же, сделает al oop.

1 Ответ

0 голосов
/ 02 июня 2020

Вот что я придумал:

public enum Bar {
    @frozen
    public struct Id {
        private var _id: String = ""
        public var id: String {
            get { _id }
            set { _id = newValue }
        }
        public init(id: String) {
            _id = id
        }
    }

    @frozen
    public struct Bar {
        public var id: Id
    }
}

// MARK: - Protocols

protocol Id {
    var id: String { get }
}

protocol Foo {
    var id: Id { get }
}

// MARK: - Conformance

// (1) Declare that the type conforms to the protocol 
//     (won't compile without the second extension below)
extension Bar.Id: Id {}

// (2) Extend the protocol enough to ensure the compiler
//     knows how the protocol conformance declared in (1)
//     is supposed to actually work.  
extension Id where Self == Bar.Id {
    var id: String { self.id }
    init(id: String) {
        self.init(id: id)
    }
}

// (1) Declare that the type conforms to the protocol 
//     (won't compile without the second extension below)
extension Bar.Bar: Foo {}

// (2) Extend the protocol enough to ensure the compiler
//     knows how the protocol conformance declared in (1)
//     is supposed to actually work.  
extension Foo where Self == Bar.Bar {
    var id: Id { self.id }
}

Ключ в том, что вам нужны два расширения: одно, которое объявляет, что тип соответствует протоколу, и другое, расширяющее протокол с предложением where и обеспечивает необходимый клей, чтобы убедить компилятор, что требования протокола удовлетворяются рассматриваемым типом.

Это работает только потому, что тела функций / средства доступа к свойствам внутри расширений протокола имеют особая способность, которая характерна только для расширений протокола (но не для расширений структур / классов).

Эта особая способность расширений протокола заключается в том, что реализация в расширении может получить доступ к исходной реализации структуры / класса той же переменной или функции , объявленной внутри любого типа, который соответствует протоколу, расширяется и соответствует требованиям пункта where.

Из-за этой особой способности расширений протокола следующее не является бесконечным l oop:

extension Foo where Self == Bar.Bar {
    var id: Id { self.id }
}
...