无法在 Swift 中的另一个协议中使用协议作为关联类型 [英] Unable to use protocol as associatedtype in another protocol in Swift
问题描述
我有一个协议,Address
,它继承自另一个协议,Validator
,Address
实现了Validator
> 扩展中的要求.
I have a protocol, Address
, which inherits from another protocol, Validator
, and Address
fulfills the Validator
requirement in the extension.
还有另一个协议,FromRepresentable
,它有一个 associatedType
(ValueWrapper
) 要求,它应该是 Validator
.
There is another protocol, FromRepresentable
, which has an associatedType
(ValueWrapper
) requirement which should be Validator
.
现在,如果我尝试使用 Address
作为 associatedType
,那么它不会编译.它说,
Now if I try to use Address
as associatedType
, then it does not compile. It says,
推断类型地址"(通过匹配要求valueForDetail")是无效:不符合验证器".
Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform to 'Validator'.
这种用法是否违法?难道我们不能使用Address
代替Validator
,因为所有Addresses
都是Validator
.
Is this usage illegal? Shouldn't we be able to use Address
in place of Validator
, as all Addresses
are Validator
.
下面是我正在尝试的一段代码.
Below is the piece of code I am trying.
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
约束到一个特定的(非 @objc
)协议,你就必须使用一个具体的类型来满足这个要求.
The problem, which David has already alluded to, is that once you constrain a protocol's associatedtype
to a specific (non @objc
) protocol, you have to use a concrete type to satisfy that requirement.
这是因为协议不符合自身 – 因此意味着您不能使用 Address
来满足符合 Validator
的类型的协议关联类型要求,因为 Address
是 not符合 Validator
的类型.
This is because protocols don't conform to themselves – therefore meaning that you cannot use Address
to satisfy the protocol's associated type requirement of a type that conforms to Validator
, as Address
is not a type that conforms to Validator
.
正如我在在这里的回答中演示的那样,请考虑以下反例:
As I demonstrate in my answer here, consider the counter-example of:
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
}
最简单的解决方案是在 ValueWrapper
关联类型上放弃 Validator
协议约束,允许您在方法参数中使用抽象类型.
The simplest solution would be to ditch the Validator
protocol constraint on your ValueWrapper
associated type, allowing you to use an abstract type in the method argument.
protocol FormRepresentable {
associatedtype ValueWrapper
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
enum AddressFrom : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: Address) -> String {
// ...
}
}
如果您需要关联的类型约束,并且每个 AddressFrom
实例只需要 Address
的单个具体实现作为输入 - 您可以使用泛型来为您的 AddressFrom
使用在您的方法中使用的给定具体地址类型进行初始化.
If you need the associated type constraint, and each AddressFrom
instance only expects a single concrete implementation of Address
as an input – you could use generics in order for your AddressFrom
to be initialised with a given concrete type of address to be used in your method.
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
但是,如果您同时需要关联类型约束和,则每个 AddressFrom
实例必须能够处理任何类型的 Address
输入– 您将使用类型擦除以将任意 Address
包装在具体类型中.
However, if you require both the associated type constraint and each AddressFrom
instance must be able to handle an input of any type of Address
– you'll have use a type erasure in order to wrap an arbitrary Address
in a concrete type.
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))
这篇关于无法在 Swift 中的另一个协议中使用协议作为关联类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!