2015-08-01 15 views
5

Stworzyłem 2 protokoły z powiązanymi typami. Typ zgodny z Reader powinien być w stanie wytworzyć instancję typu zgodnego z Value.Protokół Swift z ograniczonym typem błędu "Typ nie jest wymienialny"

Warstwa złożoności wynika z rodzaju zgodnego Manager powinny być w stanie wytworzyć konkretną Reader wystąpienie który wytwarza konkretny typ Value (albo Value1 lub Value2).

Z moją konkretną implementacją Manager1 chciałbym, aby zawsze produkować Reader1, która z kolei produkuje instancje Value1.

Może ktoś wyjaśnić, dlaczego

"Reader1 is not convertible to ManagedReaderType?"

Gdy błędna linia zmienia się na (na razie) powrócić nil wszystko kompiluje dobrze, ale teraz nie mogę instancję albo Reader1 lub Reader2.

Poniższa może być wklejony do placu zabaw, aby zobaczyć błąd:

import Foundation 

protocol Value { 
    var value: Int { get } 
} 

protocol Reader { 
    typealias ReaderValueType: Value 
    func value() -> ReaderValueType 
} 

protocol Manager { 
    typealias ManagerValueType: Value 

    func read<ManagerReaderType: Reader where ManagerReaderType.ReaderValueType == ManagerValueType>() -> ManagerReaderType? 
} 

struct Value1: Value { 
    let value: Int = 1 
} 

struct Value2: Value { 
    let value: Int = 2 
} 

struct Reader1: Reader { 
    func value() -> Value1 { 
     return Value1() 
    } 
} 

struct Reader2: Reader { 
    func value() -> Value2 { 
     return Value2() 
    } 
} 

class Manager1: Manager { 
    typealias ManagerValueType = Value1 

    let v = ManagerValueType() 
    func read<ManagerReaderType: Reader where ManagerReaderType.ReaderValueType == ManagerValueType>() -> ManagerReaderType? { 
     return Reader1()// Error: "Reader1 is not convertible to ManagedReaderType?" Try swapping to return nil which does compile. 
    } 
} 

let manager = Manager1() 
let v = manager.v.value 
let a: Reader1? = manager.read() 
a.dynamicType 
+0

typalias ManagerValueType: Wartość jest niepoprawna - definiujesz typ z "=", np. Typalias ManagerValueType = Wartość –

+0

'typalias ManagerValueType: Wartość' w protokole' Manager' to ograniczenie, * nie * ustawienie typu. – Jay

+0

Czy ten kod jest tylko krótkim przykładem większego projektu lub czy jest to Twoja ciekawość, jak rozwiązać ten problem? – Qbyte

Odpowiedz

3

Błąd występuje, ponieważ ManagerReaderType w funkcji read jest tylko ogólna zastępczy dla każdego typu, który odpowiada Reader i jego ReaderValueType jest równa do tego z ManagerReaderType. Więc rzeczywisty typ ManagerReaderType nie jest określana przez samą funkcję, zamiast typ zmiennej, która zostanie przypisany deklaruje typ:

let manager = Manager1() 
let reader1: Reader1? = manager.read() // ManagerReaderType is of type Reader1 
let reader2: Reader2? = manager.read() // ManagerReaderType is of type Reader2 

jeśli wrócisz nil może być przekształcony do dowolnego rodzaju, dlatego zawsze działa .

Jako alternatywę można powrócić konkretny typ typu Reader:

protocol Manager { 
    // this is similar to the Generator of a SequenceType which has the Element type 
    // but it constraints the ManagerReaderType to one specific Reader 
    typealias ManagerReaderType: Reader 

    func read() -> ManagerReaderType? 
} 

class Manager1: Manager { 

    func read() -> Reader1? { 
     return Reader1() 
    } 
} 

Jest to najlepsze podejście z protokołami z powodu braku „prawdziwych” rodzajowych (z poniższych nie jest obsługiwana (jeszcze)):

// this would perfectly match your requirements 
protocol Reader<T: Value> { 
    fun value() -> T 
} 

protocol Manager<T: Value> { 
    func read() -> Reader<T>? 
} 

class Manager1: Manager<Value1> { 
    func read() -> Reader<Value1>? { 
     return Reader1() 
    } 
} 

Więc najlepiej byłoby obejście aby Reader ogólny klasy i podklasy Reader1 i Reader2 specyficzny typ rodzajowy od niego:

class Reader<T: Value> { 
    func value() -> T { 
     // or provide a dummy value 
     fatalError("implement me") 
    } 
} 

// a small change in the function signature 
protocol Manager { 
    typealias ManagerValueType: Value 
    func read() -> Reader<ManagerValueType>? 
} 

class Reader1: Reader<Value1> { 
    override func value() -> Value1 { 
     return Value1() 
    } 
} 

class Reader2: Reader<Value2> { 
    override func value() -> Value2 { 
     return Value2() 
    } 
} 

class Manager1: Manager { 
    typealias ManagerValueType = Value1 

    func read() -> Reader<ManagerValueType>? { 
     return Reader1() 
    } 
} 

let manager = Manager1() 

// you have to cast it, otherwise it is of type Reader<Value1> 
let a: Reader1? = manager.read() as! Reader1? 

Ta implementacja powinna rozwiązać problem, ale Readers są teraz typami odniesienia i należy wziąć pod uwagę funkcję kopiowania.

Powiązane problemy