2016-09-19 2 views
4

verwendet werden Ich möchte eine Klasse mit statischen Initialisierungsmethode haben:‚Self‘ kann nicht in nicht-trivialen Verschlüsse

class A { 

    required init() { 
    } 

    // this one works 
    class func f0() -> Self { 
    return self.init() 
    } 

    // this one works as well  
    class func f1() -> Self { 
    let create = { self.init() } // no error, inferred closure type is '() -> Self' 
    return create() 
    } 
} 

Leider ist Swift 3-Compiler nicht in der Lage Art für jede Schließung komplexer als { self.init() } schließen . Zum Beispiel:

class func f2() -> Self { 
    let create = { 
    // error: unable to infer complex closure return type; add explicit type to disambiguate 
    let a = self.init() 
    return a 
    } 

    return create() 
} 

Jeder Versuch, Verschlusstyp, Variablentyp explizit oder Guss A-Self führt zu einem Fehler angeben:

class func f3() -> Self { 
    let create = {() -> Self in // error: 'Self' is only available in a protocol or as the result of a method in a class; 
    let a = self.init() 
    return a 
    } 

    return create() 
} 

class func f4() -> Self { 
    let create = { 
    let a: Self = self.init() // error: 'Self' is only available in a protocol or as the result of a method in a class; 
    return a 
    } 

    return create() 
} 

class func f5() -> Self { 
    let create = {() -> A in 
    let a = self.init() 
    return a 
    } 

    return create() as! Self // error: cannot convert return expression of type 'A' to return type 'Self' 
} 

Die Lösung ist Schließungen zu vermeiden Self verwenden.

Dies scheint wie eine sehr unglückliche Einschränkung des Compilers. Gibt es einen Grund dafür? Wird dieses Problem wahrscheinlich in zukünftigen Swift-Versionen behoben?

+1

Obwohl nicht identisch, ist dies sehr ähnlich zu http://stackoverflow.com/questions/25645090/Protokoll-Func-Rückkehr-Selbst. Das grundlegende Problem besteht darin, dass Swift den Kompilierertyp selbst bestimmen muss, aber zur Laufzeit bestimmen will. Es wurde etwas erweitert, damit Klassenfunktionen Self ausführen können, wenn alles zur Kompilierungszeit aufgelöst werden kann, aber Swift kann nicht immer beweisen, dass Ihre Typen * in einigen dieser Fälle korrekt sein müssen (manchmal weil es nicht weiß, wie noch, und manchmal, weil es tatsächlich möglich ist, falsch zu liegen). –

+0

Ich erwarte, dass dies ein wenig besser wird, aber Swift entmutigt diese Art von komplexer Vererbung (das sind nicht endgültige Klassen, also müssen Sie alle möglichen Unterklassen berücksichtigen) und bevorzugt Protokolle langfristig, also würde ich das nicht erwarten in Swift 4 oder 5 vollständig möglich sein. –

+0

@RobNapier _Swift kann nicht immer beweisen, dass Ihre Typen in einigen dieser Fälle korrekt sein müssen (manchmal weil es noch nicht weiß, und manchmal, weil es tatsächlich möglich ist, falsch zu liegen) Gibt es Beispiele für solche Fälle? –

Antwort

2

Das grundlegende Problem ist, dass Swift den Typ von Self zur Kompilierzeit bestimmen muss, aber Sie möchten es zur Laufzeit bestimmen. Es wurde etwas erweitert, damit Klassenfunktionen Self zurückgeben können, wenn alles zur Kompilierzeit aufgelöst werden kann, aber Swift kann nicht immer beweisen, dass Ihre Typen in einigen dieser Fälle korrekt sein müssen (manchmal, weil es noch nicht weiß, und manchmal, weil es tatsächlich möglich ist, falsch zu liegen).

Betrachten Sie als Beispiel eine Unterklasse, die eine Methode zur Selbstrückgabe nicht überschreibt. Wie kann es die Unterklasse zurückgeben? Was, wenn es Selbst an etwas anderes weitergibt? Wie kann man statisch typisieren, dass zur Kompilierzeit gegebene zukünftige Unterklassen dem Compiler gar nicht bekannt sind? (Einschließlich Unterklassen, die zur Laufzeit und über Modulgrenzen hinweg hinzugefügt werden können.) Ich erwarte, dass dies ein wenig besser wird, aber Swift rät von dieser Art von komplexer Vererbung ab (dies sind nicht endgültige Klassen, also müssen Sie alle berücksichtigen.) mögliche Unterklassen) und bevorzugt Protokolle langfristig, so würde ich nicht erwarten, dass dies in Swift 4 oder 5 vollständig möglich ist.