2015-03-14 17 views
5

Sto cercando di dichiarare una funzione in un protocollo che costringe tipi conformi ad esso per restituire un valore dello stesso protocollo, ma con uno specifico tipo associato:tipi associati e generici su protocolli

protocol Protocol { 
    typealias ValueType 

    var value : ValueType? {get} 

    func getProtocolString<A where A : Protocol, A.ValueType == String>() -> A 
} 

Questo compila. E 'quando cerco di creare una classe che si conforma ad esso che ottengo gli errori:

class AClass<T> : Protocol { 
    var value : T?  

    func getProtocolString<A where A : Protocol, A.ValueType == String>() -> A { 
     return AClass<String>() 
    } 
} 

L'errore è 'AClass' non è convertibile in 'A'.

Mi manca qualcosa? È possibile?

Grazie

+0

Si dichiara un protocollo che ha un tipo _Generic_ ValueType'. Quindi, per conformarsi al protocollo stesso, si deve avere 'ValueType == String'. Quindi 'ValueType' non è destinato a essere _generico_. Si noti che 'String' non è un protocollo, ma un tipo concreto. –

risposta

2

Il problema sta nel confondere un segnaposto generico vincolato da un protocollo, con il protocollo stesso. Ecco un esempio più semplice, simile al tuo codice, per provare e chiarire:

// first, define a protocol and two structs that conform to it 
protocol P { } 
struct S1: P { } 
struct S2: P { } 

// now, a function that returns an object in the form 
// of a reference to protocol P 
func f() -> P { 
    // S1 conforms to P so that’s fine 
    return S1() 
} 
// ok all well and good, this works fine: 
let obj = f() 

// now, to do something similar to your example code, 
// declare a generic function that returns a generic 
// placeholder that is _constrained_ by P 
// This will NOT compile: 
func g<T: P>() -> T { return S1() } 

Perché questo non viene compilato?

Il modo funzioni generiche lavoro è che al momento della compilazione, quando si chiama la funzione, il compilatore decide quale digitare il segnaposto T deve essere, e poi si scrive una funzione con tutte le occorrenze di T sostituiti con quel tipo .

Quindi, con l'esempio di seguito, T dovrebbe essere sostituito da S1:

let obj1: S1 = g() 
// because T needs to be S1, the generic function g above is 
// rewritten by the compiler like this: 
func g() -> S1 { return S1() } 

Questo sembra OK. Tranne, e se volessimo T essere S2? S2 è conforme a P quindi è un valore perfettamente legittimo per T. Ma come potrebbe funzionare:

// require our result to be of type S2 
let obj2: S2 = g() 
// so T gets replaced with S2… but now we see the problem. 
// you can’t return S1 from a function that has a return type of S2. 
// this would result in a compilation error that S2 is not 
// convertible to S1 
func g() -> S2 { return S1() } 

Ecco l'origine del messaggio di errore che si sta ottenendo. Il segnaposto A può sostituire qualsiasi tipo conforme a Protocol, ma si sta tentando di restituire un specifico tipo (AClass) conforme a tale protocollo. Quindi non ti lascerà fare.

+0

Risposta perfetta, davvero ben spiegata. Mi mancava il fatto che i generici di funzioni funzionano in fase di compilazione. Grazie. – fbernardo

0

Sembra che siete un po 'generici malinteso. Le funzioni generiche vengono istanziate nei siti di chiamata di questi, non in ogni corpo della stessa funzione. Quindi, i vincoli di tipo che hai scritto stanno dicendo che questa funzione restituisce un valore, il cui tipo può essere uno qualsiasi dei sottotipi di Protocol. Di conseguenza, la definizione della funzione deve essere staticamente corretta su A per tutti i sottotipi di Protocol, non solo per AClass<String>, che è solo un tipo di Protocol.

In ogni caso, penso che non ci sia un modo diretto per ottenere ciò che si vuole, almeno nella corrente Swift.

0

Questo sembrava funzionare nel parco giochi ... funziona per quello che stai cercando di fare?

protocol StringProtocol 
{ 
    typealias ValueType 

    var value : ValueType? { get } 

    func getProtocolString<A where A: StringProtocol, A.ValueType == String>() -> A 
} 

class StringClass : StringProtocol 
{ 
    typealias ValueType = String 

    var value : ValueType? 

    init() { } 

    func getProtocolString<A where A: StringProtocol, A.ValueType == String>() -> A 
    { 
     return StringClass() as A 
    } 
} 

Non sto ancora seguendo esattamente quali requisiti si sta cercando di soddisfare con questa implementazione.