2016-05-21 2 views
6

У меня есть протокол, Address, который наследует от другого протокола, Validator и Address выполняет Validator требования в расширении.Невозможно использовать протокол как associatedtype в другом протоколе в Swift

Существует другой протокол, FromRepresentable, который имеет associatedType (ValueWrapper) требование, которое должно быть Validator.

Теперь, если я попытаюсь использовать Address как associatedType, то он не компилируется. Он говорит,

Inferred типа 'Адрес' (путем сопоставления требования '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 
     } 
    } 
} 

Update: Подал bug.

ответ

4

Проблема, которая David has already eluded to, что как только вы ограничьте протокол associatedtype конкретным протоколом, вы должны использовать конкретный тип для удовлетворения этого требования
.

Это происходит потому, что protocols don't conform to themselves - поэтому это означает, что вы не можете использовать Address удовлетворить соответствующий тип требованию Протокола типа, который соответствует Validator, поскольку Address является не тип, который соответствует Validator.

Как я демонстрирую in my answer here, рассмотрим встречный пример:

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

let addressFrom = AddressFrom.Address1 

let address = ShippingAddress(addressLine1: "", city: "", country: "") 

addressFrom.valueForDetail(AnyAddress(address)) 
+0

Хорошо, я понял, но есть ли причина, почему мы должны использовать конкретный тип для связанного типа с ограничением протокола? Компилятор не выдает никаких ошибок, если мы используем Address в качестве аргумента для функции, которая ожидает Validator. –

+0

Я не понимаю, почему мы должны использовать конкретный тип, если у связанного типа есть ограничение по протоколу. Имеет ли это какое-то отношение к поведению инвариантности или распределению памяти? У вас есть ссылки, где я могу получить дополнительную информацию об этом. –

+0

@ VishalSingh Боюсь, я не могу предложить лучшую причину, чем «потому что это так, как есть» - я, конечно, не могу придумать, почему это не должно быть возможно, потому что безусловная версия работает нормально , Я также не могу найти что-либо в списке рассылки быстрой эволюции или об ошибке. Возможно, стоит записать [отчет об ошибке] (https://bugs.swift.org/secure/Dashboard.jspa) и посмотреть, что они говорят об этом. – Hamish

0

У вас есть несколько вопросов:

Прежде всего, вы на самом деле не объявить, что адрес реализует валидатора

//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 
     } 
    } 
} 
+0

Эй s orry Я отправил этот вопрос, пока я все еще пытался заставить его работать. Я отредактировал фрагмент кода. Адрес фактически наследуется от Validator. Что касается типов, я думаю, что он должен иметь возможность вывести ValueWrapper из функции valueForDetail (_: Address). Я не хочу, чтобы это было конкретным типом. –

+0

Обратите внимание, что на самом деле вам не нужно явно указывать 'связанный тип протокола '(через' typealias') в вашей реализации, Swift может вывести его из типа аргумента метода. – Hamish

Смежные вопросы