无法在Swift

时间:2016-05-21 07:23:29

标签: ios swift swift-protocols associated-types

我有一个协议Address,它继承自另一个协议Validator,而Address符合扩展中的Validator要求。

还有另一个协议FromRepresentable,其associatedTypeValueWrapper)要求应为Validator

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

  

推断类型'地址' (通过匹配要求' valueForDetail')是   无效:不符合'验证者'。

这种用法是非法的吗?我们不能使用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
        }
    }
}

更新:提交bug.

2 个答案:

答案 0 :(得分:5)

David has already alluded to的问题是,一旦您将协议的associatedtype约束到特定的(非@objc)协议,您必须使用具体的类型来满足这个要求。

这是因为protocols don't conform to themselves - 因此意味着您无法使用Address来满足符合Validator类型的协议的关联类型要求,因为{{1 符合Address的类型。

在我演示in my answer here时,请考虑反例:

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 } 关联类型的Validator协议约束,允许您在方法参数中使用抽象类型。

ValueWrapper

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

如果您需要关联的类型约束,并且每个enum AddressFrom : Int, FormRepresentable { // ... func valueForDetail(valueWrapper: Address) -> String { // ... } } 实例只需要AddressFrom的单个具体实现作为输入 - 您可以使用泛型来使Address成为使用给定的具体类型的地址初始化,以便在您的方法中使用。

AddressFrom

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

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 {
        // ...
    }
}

答案 1 :(得分:0)

您有几个问题:

首先,您实际上并未声明Address实现Validator

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

并且您没有声明ValueWrapper的关联类型:

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