2016-05-21 9 views
6

Mam protokół, Address, która dziedziczy z innego protokołu, Validator i Address spełnia wymóg w rozszerzeniu Validator.Nie można używać protokołu jako associatedtype w innym protokole w Swift

Istnieje inny protokół, FromRepresentable, który ma wymaganie associatedType (ValueWrapper), które powinno być Validator.

Teraz, jeśli spróbuję użyć Address jako associatedType, to się nie skompiluje. Mówi,

Wnioskowany typ „Address” (dopasowując wymóg „valueForDetail”) jest nieprawidłowy: nie spełnia „Weryfikator”.

Czy korzystanie z tego urządzenia jest nielegalne? Nie powinniśmy móc używać Address w miejsce Validator, ponieważ wszystkie Addresses są.

Poniżej znajduje się fragment kodu, który próbuję.

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

Aktualizacja: złożyła bug.

Odpowiedz

4

problemu, który David has already eluded to jest, że po ograniczyć protokół associatedtype do określonego protokołu, musisz użyć typu betonu, aby spełnić to wymaganie
.

To dlatego protocols don't conform to themselves - więc co oznacza, że ​​nie można używać Address zaspokoić że w protokole powiązanych wymóg dla typu, który odpowiada Validator, jak Address jest nie typ, który jest zgodny z Validator.

Jak wykazać in my answer here rozważyć kontr-przykład:

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 
} 

Najprostszym rozwiązaniem byłoby rów ograniczenie protokołu Validator na ValueWrapper powiązanego typu, co pozwala na użycie abstrakcyjne typu w metodzie argument.

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

enum AddressFrom : Int, FormRepresentable { 

    // ... 

    func valueForDetail(valueWrapper: Address) -> String { 
     // ... 
    } 
} 

Jeśli potrzebujesz wiązanego typu ograniczenia, a każde wystąpienie AddressFrom oczekuje tylko jeden konkretny realizację Address jako wejście - można użyć rodzajowych, aby Twój AddressFrom do być zainicjalizowane konkretnym typem adresu, który ma być użyty w twojej metodzie.

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 

Jednakże, jeśli wymagają zarówno wiązanego typu ograniczenie i każda instancja AddressFrom musi być w stanie obsługiwać dane wejściowe z dowolny typ Address - będziesz musiał użyć typu wymazania, aby zawinąć dowolny typ Address w konkretny typ.

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

Dobra, rozumiem, ale czy jest jakiś powód, dla którego musimy użyć concreteType dla relatedType z ograniczeniem protokołu? Kompilator nie wydaje żadnego błędu, jeśli użyjemy adresu jako argumentu dla funkcji, która oczekuje walidatora. –

+0

Nie rozumiem tego, dlaczego musimy użyć konkretnego typu, jeśli relatedType ma ograniczenie protokołu. Czy ma to coś wspólnego z zachowaniem niezmienniczym lub alokacją pamięci? Czy masz jakieś odniesienia, gdzie mogę więcej informacji na ten temat. –

+0

@VishalSingh Obawiam się, że nie mogę zaoferować lepszego powodu niż "ponieważ tak właśnie jest" - na pewno nie mogę wymyślić powodu, dla którego nie byłoby to możliwe, ponieważ ta nieskrępowana wersja działa dobrze . Nie mogę też znaleźć niczego na liście mailingowej szybkiego śledzenia ewolucji ani na temat tego narzędzia do śledzenia błędów. Warto również zgłosić [zgłoszenie błędu] (https://bugs.swift.org/secure/Dashboard.jspa) i zobaczyć, co o nim mówią. – Hamish

0

masz kilka kwestii:

Przede wszystkim, w rzeczywistości nie deklarują tego adresu realizuje Validator

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

I nie zadeklaruj skojarzony typ dla ValueWrapper:

typealias ValueWrapper = ShippingAddress 

I to wydaje się być faktycznie chcąc AddressFrom.valueForDetail wziąć ShippingAddress:

func valueForDetail(valueWrapper: ShippingAddress) -> String { 
    switch self { 
    case .Address1: 
     return valueWrapper.addressLine1 
    case .City: 
     return valueWrapper.city 
    case .Country: 
     return valueWrapper.country 
    } 
} 

sumie to wygląda:

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

Cześć s Orry Wysłałem to pytanie, gdy wciąż próbowałem sprawić, by działało. Edytowałem fragment kodu. Adres faktycznie dziedziczy z Validatora. Jeśli chodzi o typy, myślę, że powinien być w stanie wywnioskować ValueWrapper z funkcji valueForDetail (_: Address). Nie chcę, żeby to był konkret konkretu. –

+0

Zauważ, że nie musisz w sposób jawny definiować typu powiązanego 'protocol type (typu" typealias') w swojej implementacji, Swift może wywnioskować go z typu argumentu method. – Hamish

Powiązane problemy