2014-12-02 11 views
13

Sto cercando di capire i limiti esatti sull'enumerazione con valori associati generici in Swift.Qual è la limitazione esatta dei valori associati generici nelle enumerazioni Swift?

Si potrebbe pensare che siano supportati, poiché Optional è un tipo simile. Qui è il codice definisce Optional nella libreria standard Swift:

enum Optional<T> : Reflectable, NilLiteralConvertible { 
    case None 
    case Some(T) 
// ... 
} 

Sembra che il membro caso Some ha un valore associato di tipo variabile T, giusto?

Tuttavia, è menzionato nel libro Functional Programming in Swift (p 87), che tali tipi non sono supportati:

Vorremmo definire una nuova enumerazione che è generico nel risultato associato con successo:

enum Result<T> { 
    case Success(T) 
    case Failure(NSError) 
}

Sfortunatamente, i valori associati generici non sono supportati dal compilatore Swift corrente.

E infatti, se si digita lo snippet nel compilatore, si riceve un errore (error: unimplemented IR generation feature non-fixed multi-payload enum layout).

Quindi cosa sta succedendo qui? È solo che non è supportato in generale, ma è supportato per Optional come caso speciale? C'è un modo per vedere come Optional riceve questo supporto speciale? O se anche altri tipi di libreria standard ottengono un supporto speciale?

+1

Sembra che un valore * generico associato sia possibile solo se è il valore associato * * *. –

+0

Oh giusto. Quindi, nell'errore, "multi-payload" si riferisce al fatto che esiste più di un valore associato. E che dire di "non fisso"? Direi che ciò si riferisce al fatto che la dimensione di 'T' è indeterminata, poiché se si tratta di un tipo non di classe, non si può certamente sapere che abbia le dimensioni di un puntatore a oggetti. – algal

risposta

6

Questa risposta non è aggiornata in Swift 2. Vedere la risposta di rickster per gli aggiornamenti di Swift 2.

I vostri commenti sono corretti. Non è possibile avere più casi con dati associati se uno di essi ha dimensioni sconosciute. I tipi di valore possono essere di qualsiasi dimensione (dal momento che sono copiati). I tipi di riferimento (come gli oggetti) hanno una dimensione nota, perché memorizzano un puntatore.

La soluzione tipica è creare una classe wrapper aggiuntiva per contenere il tipo generico, come fa il libro FP. Tutti lo chiamano per convenzione Box. C'è motivo di sperare che il team di Swift risolverà questo problema in futuro. Come si nota, si riferiscono ad esso come "non implementato" e non "non supportato".

Una tipica implementazione di Box:

final public class Box<T> { 
    public let unbox: T 
    public init(_ value: T) { self.unbox = value } 
} 
18

In Swift 2 (attualmente in beta come parte di Xcode 7), non v'è alcuna limitazione valori associati. Quindi, sentitevi liberi di ballare a ritmi così:

enum YouCanGoWith<T, U> { 
    case This(T) 
    case That(U) 
    case Us 
} 

Ora, se siete alla ricerca di una sorta successo-o-Errore di enum, si potrebbe desiderare di fermarsi e riflettere sul perché ... perché Swift 2 porta anche un nuovo modello di gestione degli errori. Quindi non hai bisogno di un tale tipo come valore di ritorno delle vostre funzioni - si può semplicemente dichiarare in questo modo:

func walkWith(rhythm: Bool) throws -> Place { /* ... */ } 

... e se la vostra funzione ha esito positivo, il chiamante ottiene sempre una (non-optional) Place per camminare verso. E - separatamente dall'utilizzo del risultato - il chiamante decide come gestire, ingoiare o propagare l'errore.

Per ulteriori dettagli, vedere Error Handling in Lingua di programmazione Swift. Osserva da vicino - la sintassi assomiglia un po 'al modello di eccezione che vedi in alcuni altri linguaggi, ma gli errori di Swift sono un tipo di animale completamente diverso.

(Naturalmente, il modello throws è specifico per le chiamate sincrone Se invece si sta dichiarando callback per i processi asincroni, in cui la chiusura di callback riceve sia il risultato di un lavoro asincrono successo o un errore -. Un successo-o-errore il tipo è ancora del tutto appropriato)

+0

La mia esperienza con i parchi giochi è che non sempre ti danno errori corretti per il compilatore. Quindi qualcosa che sembra compilare non significa che sia effettivamente funzionante. Devi aggiungere qualche valore aggiuntivo alla fine e assicurarti che si presenti davvero con un valore per dimostrare che il parco giochi è in esecuzione. Questo è doppiamente vero quando si blocca il compilatore (che questo tipo di cose ancora fa spesso). Quasi sempre faccio lavori non banali (leggi: tutto) in piccole app a linea di comando a causa di ciò. –

+0

L'errore e l'esecuzione corretta dei casi di test sono piuttosto diversi, tuttavia. Nuked quella parte per ora per evitare confusione. – rickster

+0

Purtroppo, sebbene il compilatore ti permetta di dichiarare l'enum in questo modo, non ti permetterà di vincolarlo in un'istruzione switch; Ho provato un certo numero di variazioni su 'switch risultato {case let .Success (data): ...}', tutte fallite con l'utile (/ s) avviso "Comando .../bin/swiftc fallito con codice di uscita 1 ". Non ci sono ulteriori avvisi del compilatore che indicano quale parte della corrispondenza del modello fa abortire il compilatore. –

Problemi correlati