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

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

问题描述

我有一个协议,地址,它继承自另一个协议, Validator 地址满足扩展中的 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.

现在如果我尝试使用地址作为 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'.

这种用法是非法的吗?我们不应该使用地址代替 Validator ,因为所有地址 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.

这是因为协议不符合自己 - 因此意味着你不能使用地址 t o满足协议的相关类型要求,类型符合 Validator ,因为地址 符合 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.

当我演示在我的回答中,考虑反例:

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
}

最简单的解决方案是放弃<$ c您的 ValueWrapper 关联类型的$ c> 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 实例只需要地址的单个具体实现作为输入 - 您可以使用泛型来为您的 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 实例必须能够处理任何类型的地址的输入 - 你将拥有使用类型擦除以在具体类型中包装任意地址

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天全站免登陆