无法在 Swift 中的另一个协议中使用协议作为关联类型 [英] Unable to use protocol as associatedtype in another protocol in Swift

查看:37
本文介绍了无法在 Swift 中的另一个协议中使用协议作为关联类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个协议,Address,它继承自另一个协议,ValidatorAddress 实现了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 的类型的协议关联类型要求,因为 Addressnot符合 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆