无法在Swift中将协议用作其他协议中的关联types

我有一个协议Address ,它从另一个协议inheritance, ValidatorAddress满足扩展中的Validator要求。

还有另一种协议, FromRepresentable ,它有一个associatedTypeValueWrapper )要求,应该是Validator

现在,如果我尝试使用Address作为associatedType ,那么它不会编译。 它说,

推断types“地址”(通过匹配要求'valueForDetail')是无效的:不符合'Validator'。

这个用法是非法的吗? 我们不应该使用Address来代替Validator ,因为所有的Addresses都是Validator

下面是我正在尝试的代码片段。

 enum ValidationResult { case Success case Failure(String) } protocol Validator { func validate() -> ValidationResult } //Address inherits Validator protocol Address: Validator { var addressLine1: String {get set} var city: String {get set} var country: String {get set} } ////Fulfill Validator protocol requirements in extension extension Address { func validate() -> ValidationResult { if addressLine1.isEmpty { return .Failure("Address can not be empty") } return .Success } } protocol FormRepresentable { associatedtype ValueWrapper: Validator func valueForDetail(valueWrapper: ValueWrapper) -> String } // Shipping Address conforming to Address protocol. // It should also implicitly conform to Validator since // Address inherits from Validator? struct ShippingAddress: Address { var addressLine1 = "CA" var city = "HYD" var country = "India" } // While compiling, it says: // Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform // to 'Validator'. // But Address confroms to Validator. enum AddressFrom: Int, FormRepresentable { case Address1 case City case Country func valueForDetail(valueWrapper: Address) -> String { switch self { case .Address1: return valueWrapper.addressLine1 case .City: return valueWrapper.city case .Country: return valueWrapper.country } } } 

更新:提交了一个错误。

David已经避免了这个问题,一旦你将一个协议的associatedtype约束到一个特定的协议,你必须使用一个具体的types来满足
需求。

这是因为协议不符合自己 – 因此意味着您不能使用Address来满足符合Validator的协议的关联types要求,因为Address 不是符合Validator的types。

正如我在这里的答案中所表明的那样 ,考虑下面的例子:

 protocol Validator { init() } protocol Address : Validator {} protocol FormRepresentable { associatedtype ValueWrapper: Validator } extension FormRepresentable { static func foo() { // if ValueWrapper were allowed to be an Address or Validator, // what instance should we be constructing here? // we cannot create an instance of a protocol. print(ValueWrapper.init()) } } // therefore, we cannot say: enum AddressFrom : FormRepresentable { typealias ValueWrapper = Address } 

最简单的解决scheme是在你的ValueWrapper关联types上ValueWrapper Validator协议约束,允许你在方法参数中使用一个抽象types。

 protocol FormRepresentable { associatedtype ValueWrapper func valueForDetail(valueWrapper: ValueWrapper) -> String } 

 enum AddressFrom : Int, FormRepresentable { // ... func valueForDetail(valueWrapper: Address) -> String { // ... } } 

如果您需要关联的types约束,并且每个AddressFrom实例只需要Address的一个具体实现作为input – 您可以使用generics为了使AddressFrom初始化为在方法中使用给定的具体types的地址。

 protocol FormRepresentable { associatedtype ValueWrapper : Validator func valueForDetail(valueWrapper: ValueWrapper) -> String } 

 enum AddressFrom<T : Address> : Int, FormRepresentable { // ... func valueForDetail(valueWrapper: T) -> String { // ... } } 

 // replace ShippingAddress with whatever concrete type you want AddressFrom to use let addressFrom = AddressFrom<ShippingAddress>.Address1 

但是,如果您需要关联的types约束, 并且每个AddressFrom实例必须能够处理任何types的Address的input – 您将使用types擦除来包装具体types中的任意Address

 protocol FormRepresentable { associatedtype ValueWrapper : Validator func valueForDetail(valueWrapper: ValueWrapper) -> String } 

 struct AnyAddress : Address { private var _base: Address var addressLine1: String { get {return _base.addressLine1} set {_base.addressLine1 = newValue} } var country: String { get {return _base.country} set {_base.country = newValue} } var city: String { get {return _base.city} set {_base.city = newValue} } init(_ base: Address) { _base = base } } 

 enum AddressFrom : Int, FormRepresentable { // ... func valueForDetail(valueWrapper: AnyAddress) -> String { // ... } } 

 let addressFrom = AddressFrom.Address1 let address = ShippingAddress(addressLine1: "", city: "", country: "") addressFrom.valueForDetail(AnyAddress(address)) 

你有几个问题:

首先,你并没有实际声明该地址实现了validation器

 //Address inherits Validator protocol Address : Validator { var addressLine1: String {get set} var city: String {get set} var country: String {get set} } 

而且您不要声明ValueWrapper的关联types:

 typealias ValueWrapper = ShippingAddress 

而你似乎实际上想要AddressFrom.valueForDetail采取ShippingAddress

 func valueForDetail(valueWrapper: ShippingAddress) -> String { switch self { case .Address1: return valueWrapper.addressLine1 case .City: return valueWrapper.city case .Country: return valueWrapper.country } } 

总的来说,它看起来像:

 enum ValidationResult { case Success case Failure(String) } protocol Validator { func validate() -> ValidationResult } //Address inherits Validator protocol Address : Validator { var addressLine1: String {get set} var city: String {get set} var country: String {get set} } ////Fulfill Validator protocol requirements in extension extension Address { func validate() -> ValidationResult { if addressLine1.isEmpty { return .Failure("Address can not be empty") } return .Success } } protocol FormRepresentable { associatedtype ValueWrapper: Validator func valueForDetail(valueWrapper: ValueWrapper) -> String } // Shipping Address conforming to Address protocol. // It should also implicity conform to Validator since // Address inherits from Validator? struct ShippingAddress: Address { var addressLine1 = "CA" var city = "HYD" var country = "India" } // While compiling, it says: // Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform // to 'Validator'. // But Address confroms to Validator. enum AddressFrom: Int, FormRepresentable { case Address1 case City case Country // define associated type for FormRepresentable typealias ValueWrapper = ShippingAddress func valueForDetail(valueWrapper: ShippingAddress) -> String { switch self { case .Address1: return valueWrapper.addressLine1 case .City: return valueWrapper.city case .Country: return valueWrapper.country } } }