首页 > 解决方案 > 在 Swift 中遵循具有相同变量名的协议时如何避免无限循环?

问题描述

给定 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 }
}

但是,这会导致无限循环。

如何在没有无限循环的情况下扩展 3rdPartyElement 以符合 MyModule.Element 协议...?

我也尝试像这样声明它:

extension 3rdPartyElement: MyModule.Element {}

...因为已经有id一个应该满足协议的类型的属性。但是,这也不起作用,我收到一个编译器错误,告诉我添加属性 getter,这当然会产生一个循环。

标签: swiftprotocols

解决方案


这是我想出的:

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子句要求的任何类型中声明。

由于协议扩展的这种特殊能力,以下不是无限循环:

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

推荐阅读