Was ist das Schlüsselwort "some" in Swift (UI)?

259

Das neue SwiftUI-Tutorial enthält den folgenden Code:

struct ContentView: View {
    var body: some View {
        Text("Hello World")
    }
}

In der zweiten Zeile wird das Wort someund auf ihrer Website hervorgehoben, als wäre es ein Schlüsselwort.

Swift 5.1 scheint kein someSchlüsselwort zu haben , und ich sehe nicht, was das Wort dort somesonst noch tun könnte, da es dahin geht, wo der Typ normalerweise hingeht. Gibt es eine neue, unangekündigte Version von Swift? Ist es eine Funktion, die für einen Typ auf eine Weise verwendet wird, von der ich nichts wusste?

Was macht das Schlüsselwort some?

Nikolaus
quelle
Für diejenigen, denen das Thema schwindelig war, hier ein sehr entschlüsselnder und schrittweiser Artikel dank Vadim Bulavin. vadimbulavin.com/…
Luc-Olivier

Antworten:

333

some Viewist ein undurchsichtiger Ergebnistyp, wie er von SE-0244 eingeführt wurde, und ist in Swift 5.1 mit Xcode 11 verfügbar. Sie können sich dies als einen "umgekehrten" generischen Platzhalter vorstellen.

Im Gegensatz zu einem normalen generischen Platzhalter, der vom Anrufer erfüllt wird:

protocol P {}
struct S1 : P {}
struct S2 : P {}

func foo<T : P>(_ x: T) {}
foo(S1()) // Caller chooses T == S1.
foo(S2()) // Caller chooses T == S2.

Ein undurchsichtiger Ergebnistyp ist ein impliziter generischer Platzhalter, der von der Implementierung erfüllt wird. Sie können sich also Folgendes vorstellen:

func bar() -> some P {
  return S1() // Implementation chooses S1 for the opaque result.
}

so aussehend:

func bar() -> <Output : P> Output {
  return S1() // Implementation chooses Output == S1.
}

Tatsächlich besteht das letztendliche Ziel dieser Funktion darin, umgekehrte Generika in dieser expliziteren Form zuzulassen, wodurch Sie auch Einschränkungen hinzufügen können, z -> <T : Collection> T where T.Element == Int. Weitere Informationen finden Sie in diesem Beitrag .

Die Hauptsache wegzunehmen ist , dass eine Funktion der Rückkehr some Pist ein , dass die Renditen ein Wert eines bestimmten Einzel konkreter dass Konform P. Der Versuch, verschiedene konforme Typen innerhalb der Funktion zurückzugeben, führt zu einem Compilerfehler:

// error: Function declares an opaque return type, but the return
// statements in its body do not have matching underlying types.
func bar(_ x: Int) -> some P {
  if x > 10 {
    return S1()
  } else {
    return S2()
  }
}

Da der implizite generische Platzhalter nicht von mehreren Typen erfüllt werden kann.

Dies steht im Gegensatz zu einer zurückgegebenen Funktion P, die verwendet werden kann, um beide darzustellen, S1und S2weil sie einen beliebigen Pkonformen Wert darstellt:

func baz(_ x: Int) -> P {
  if x > 10 {
    return S1()
  } else {
    return S2()
  }
}

Okay, welche Vorteile haben undurchsichtige Ergebnistypen -> some Pgegenüber Protokollrückgabetypen -> P?


1. Undurchsichtige Ergebnistypen können mit PATs verwendet werden

Eine wesentliche derzeitige Einschränkung von Protokollen besteht darin, dass PATs (Protokolle mit zugehörigen Typen) nicht als tatsächliche Typen verwendet werden können. Obwohl dies eine Einschränkung ist, die wahrscheinlich in einer zukünftigen Version der Sprache aufgehoben wird, da undurchsichtige Ergebnistypen praktisch nur generische Platzhalter sind, können sie heute mit PATs verwendet werden.

Dies bedeutet, dass Sie Dinge tun können wie:

func giveMeACollection() -> some Collection {
  return [1, 2, 3]
}

let collection = giveMeACollection()
print(collection.count) // 3

2. Undurchsichtige Ergebnistypen haben Identität

Da undurchsichtige Ergebnistypen erzwingen, dass ein einzelner konkreter Typ zurückgegeben wird, weiß der Compiler, dass zwei Aufrufe derselben Funktion zwei Werte desselben Typs zurückgeben müssen.

Dies bedeutet, dass Sie Dinge tun können wie:

//   foo() -> <Output : Equatable> Output {
func foo() -> some Equatable { 
  return 5 // The opaque result type is inferred to be Int.
}

let x = foo()
let y = foo()
print(x == y) // Legal both x and y have the return type of foo.

Das ist legal , da der Compiler weiß , dass beide xund yden gleichen Betontyp haben. Dies ist eine wichtige Voraussetzung für ==beide Parameter des Typs Self.

protocol Equatable {
  static func == (lhs: Self, rhs: Self) -> Bool
}

Dies bedeutet, dass zwei Werte erwartet werden, die beide vom gleichen Typ sind wie der konkrete konforme Typ. Selbst wenn Sie Equatableals Typ verwendet werden könnten, könnten Sie nicht zwei beliebige Equatableübereinstimmende Werte miteinander vergleichen , zum Beispiel:

func foo(_ x: Int) -> Equatable { // Assume this is legal.
  if x > 10 {
    return 0
  } else {
    return "hello world"      
  }
}

let x = foo(20)
let y = foo(5)
print(x == y) // Illegal.

Da der Compiler nicht beweisen kann, dass zwei beliebige EquatableWerte den gleichen zugrunde liegenden konkreten Typ haben.

In ähnlicher Weise, wenn wir eine andere undurchsichtige Rückgabefunktion vom Typ eingeführt haben:

//   foo() -> <Output1 : Equatable> Output1 {
func foo() -> some Equatable { 
  return 5 // The opaque result type is inferred to be Int.
}

//   bar() -> <Output2 : Equatable> Output2 {
func bar() -> some Equatable { 
  return "" // The opaque result type is inferred to be String.
}

let x = foo()
let y = bar()
print(x == y) // Illegal, the return type of foo != return type of bar.

Das Beispiel wird illegal, weil obwohl beide foound barzurückkehren some Equatable, ihre "umgekehrten" generischen Platzhalter Output1und Output2von verschiedenen Typen erfüllt werden könnten.


3. Undurchsichtige Ergebnistypen bestehen aus generischen Platzhaltern

Im Gegensatz zu regulären protokolltypisierten Werten lassen sich undurchsichtige Ergebnistypen gut mit regulären generischen Platzhaltern kombinieren, zum Beispiel:

protocol P {
  var i: Int { get }
}
struct S : P {
  var i: Int
}

func makeP() -> some P { // Opaque result type inferred to be S.
  return S(i: .random(in: 0 ..< 10))
}

func bar<T : P>(_ x: T, _ y: T) -> T {
  return x.i < y.i ? x : y
}

let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Legal, T is inferred to be the return type of makeP.

Dies hätte nicht funktioniert, wenn makePes gerade zurückgegeben worden wäre P, da zwei PWerte unterschiedliche zugrunde liegende konkrete Typen haben können, zum Beispiel:

struct T : P {
  var i: Int
}

func makeP() -> P {
  if .random() { // 50:50 chance of picking each branch.
    return S(i: 0)
  } else {
    return T(i: 1)
  }
}

let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Illegal.

Warum einen undurchsichtigen Ergebnistyp über dem Betontyp verwenden?

An diesem Punkt denken Sie sich vielleicht, warum schreiben Sie den Code nicht einfach wie folgt:

func makeP() -> S {
  return S(i: 0)
}

Durch die Verwendung eines undurchsichtigen Ergebnistyps können Sie den Typ zu Seinem Implementierungsdetail machen P, indem Sie nur die von bereitgestellte Schnittstelle verfügbar machen. So können Sie den konkreten Typ später flexibel ändern, ohne den von der Funktion abhängigen Code zu beschädigen.

Zum Beispiel könnten Sie ersetzen:

func makeP() -> some P {
  return S(i: 0)
}

mit:

func makeP() -> some P { 
  return T(i: 1)
}

ohne einen Code zu brechen, der aufruft makeP().

Siehe den Opaque Arten Abschnitt der Sprachführung und die Swift Evolution Vorschlag für weitere Informationen zu dieser Funktion.

Hamish
quelle
20
Unabhängig: Ab Swift 5.1 returnist dies für Funktionen mit einfachem Ausdruck nicht erforderlich
ielyamani
3
Aber was ist der Unterschied zwischen: func makeP() -> some Pund func makeP() -> P? Ich habe den Vorschlag gelesen und kann diesen Unterschied auch für ihre Proben nicht erkennen.
Artem
2
Swifts Typ Handling ist ein Chaos. Ist diese Spezifität wirklich etwas, das beim Kompilieren nicht behandelt werden kann? Siehe C # als Referenz, um all diese Fälle implizit durch einfache Syntax zu behandeln. Swifts müssen eine sinnlos explizite, fast frachtkultistische Syntax haben, die die Sprache wirklich verschleiert. Können Sie bitte auch die Designgründe dafür erläutern? (Wenn Sie einen Link zu dem Vorschlag in Github haben, wäre das auch gut.) Bearbeiten: Ich habe gerade bemerkt, dass er oben verlinkt ist.
SacredGeometry
2
@Zmaster Der Compiler behandelt zwei undurchsichtige Rückgabetypen als unterschiedlich, auch wenn die Implementierung für beide denselben konkreten Typ zurückgibt. Mit anderen Worten, der ausgewählte konkrete Typ ist dem Anrufer verborgen. (Ich wollte die zweite Hälfte meiner Antwort erweitern, um solche Dinge etwas expliziter zu machen, bin aber noch nicht dazu gekommen).
Hamish
52

Die andere Antwort erklärt den technischen Aspekt des neuen someSchlüsselworts gut, aber diese Antwort versucht leicht zu erklären, warum .


Angenommen, ich habe ein Protokolltier und möchte vergleichen, ob zwei Tiere Geschwister sind:

protocol Animal {
    func isSibling(_ animal: Self) -> Bool
}

Auf diese Weise ist es nur sinnvoll zu vergleichen, ob zwei Tiere Geschwister sind, wenn sie dieselbe Art von Tier sind.


Lassen Sie mich nun nur ein Beispiel für ein Tier als Referenz erstellen

class Dog: Animal {
    func isSibling(_ animal: Dog) -> Bool {
        return true // doesn't really matter implementation of this
    }
}

Der Weg ohne some T

Nehmen wir jetzt an, ich habe eine Funktion, die ein Tier aus einer 'Familie' zurückgibt.

func animalFromAnimalFamily() -> Animal {
    return myDog // myDog is just some random variable of type `Dog`
}

Hinweis: Diese Funktion wird nicht kompiliert. Dies liegt daran, dass Sie vor dem Hinzufügen der Funktion "Einige" keinen Protokolltyp zurückgeben können, wenn das Protokoll "Selbst" oder Generika verwendet . Aber nehmen wir an, Sie können ... so tun, als ob myDog den abstrakten Typ Animal überträgt. Mal sehen, was passiert

Jetzt kommt das Problem, wenn ich das versuche:

let animal1: Animal = animalFromAnimalFamily()
let animal2: Animal = animalFromAnimalFamily()

animal1.isSibling(animal2) // error

Dies wird einen Fehler auslösen .

Warum? Der Grund ist, wenn Sie animal1.isSibling(animal2)Swift anrufen, wissen Sie nicht, ob die Tiere Hunde, Katzen oder was auch immer sind. Soweit Swift kennt, animal1und animal2nicht verwandte Tierarten sein könnte . Da können wir keine Tiere verschiedener Arten vergleichen (siehe oben). Dies wird ein Fehler sein

Wie some Tlöst dieses Problem?

Schreiben wir die vorherige Funktion neu:

func animalFromAnimalFamily() -> some Animal {
    return myDog
}
let animal1 = animalFromAnimalFamily()
let animal2 = animalFromAnimalFamily()

animal1.isSibling(animal2)

animal1und animal2sind es nicht Animal , aber sie sind eine Klasse, die Animal implementiert .

Damit können Sie jetzt anrufen animal1.isSibling(animal2), Swift weiß das animal1und animal2ist vom selben Typ.

So wie ich darüber nachdenke:

some Tteilt Swift mit , welche Implementierung von Tverwendet wird, der Benutzer der Klasse jedoch nicht.

(Haftungsausschluss für Eigenwerbung) Ich habe einen Blog-Beitrag geschrieben , der etwas ausführlicher (dasselbe Beispiel wie hier) zu dieser neuen Funktion ist

Downgoat
quelle
2
Ihre Idee ist also, dass der Anrufer die Tatsache ausnutzen kann, dass zwei Aufrufe der Funktion denselben Typ zurückgeben, obwohl der Anrufer nicht weiß, um welchen Typ es sich handelt?
Matt
1
@matt im Wesentlichen yup. Gleiches Konzept bei Verwendung mit Feldern usw. - Der Aufrufer erhält die Garantie, dass der Rückgabetyp immer der gleiche Typ ist, aber nicht genau angibt, um welchen Typ es sich handelt.
Downgoat
@ Downgoat Vielen Dank für den perfekten Beitrag und die Antwort. Wie ich somein der Rückgabe verstanden habe, funktioniert der Typ als Einschränkung des Funktionskörpers. Es muss also somenur ein konkreter Typ im gesamten Funktionskörper zurückgegeben werden. Beispiel: Wenn dies der Fall ist, müssen return randomDogalle anderen Rückgaben nur mit funktionieren Dog. Alle Vorteile ergeben sich aus dieser Einschränkung: Verfügbarkeit animal1.isSibling(animal2)und Nutzen der Zusammenstellung von func animalFromAnimalFamily() -> some Animal(weil jetzt Selfunter der Haube definiert wird). Ist es richtig?
Artem
5
Diese Linie war alles, was ich brauchte, animal1 und animal2 sind nicht Animal, aber sie sind eine Klasse, die Animal implementiert, jetzt macht alles Sinn!
Aross
29

Hamishs Antwort ist ziemlich beeindruckend und beantwortet die Frage aus technischer Sicht. Ich möchte einige Gedanken dazu hinzufügen, warum das Schlüsselwort somean dieser Stelle in den SwiftUI-Tutorials von Apple verwendet wird und warum es eine gute Vorgehensweise ist, dies zu befolgen.

some ist keine Voraussetzung!

Zunächst einmal müssen Sie nicht brauchen , die zu erklären body‚s Rückgabetyp als opaken Typen. Sie können den konkreten Typ jederzeit zurückgeben, anstatt den zu verwenden some View.

struct ContentView: View {
    var body: Text {
        Text("Hello World")
    }
}

Dies wird ebenfalls kompiliert. Wenn Sie sich die ViewBenutzeroberfläche ansehen , werden Sie feststellen, dass der Rückgabetyp bodyein zugeordneter Typ ist:

public protocol View : _View {

    /// The type of view representing the body of this view.
    ///
    /// When you create a custom view, Swift infers this type from your
    /// implementation of the required `body` property.
    associatedtype Body : View

    /// Declares the content and behavior of this view.
    var body: Self.Body { get }
}

Dies bedeutet, dass Sie diesen Typ angeben, indem Sie die bodyEigenschaft mit einem bestimmten Typ Ihrer Wahl versehen. Die einzige Voraussetzung ist, dass dieser Typ das ViewProtokoll selbst implementieren muss.

Dies kann entweder ein bestimmter Typ sein, der beispielsweise implementiert Viewwird

  • Text
  • Image
  • Circle

oder ein undurchsichtiger Typ, der implementiert View, dh

  • some View

Generische Ansichten

Das Problem tritt auf, wenn wir versuchen, eine Stapelansicht als bodyRückgabetyp zu verwenden, wie VStackoder HStack:

struct ContentView: View {
    var body: VStack {
        VStack {
            Text("Hello World")
            Image(systemName: "video.fill")
        }
    }
}

Dies wird nicht kompiliert und Sie erhalten den Fehler:

Der Verweis auf den generischen Typ 'VStack' erfordert Argumente in <...>

Das ist , weil Stapel Ansichten in SwiftUI sind generische Typen! 💡 (Gleiches gilt für Listen und andere Containeransichtstypen.)

Das ist sehr sinnvoll, da Sie eine beliebige Anzahl von Ansichten eines beliebigen Typs anschließen können (sofern dies dem ViewProtokoll entspricht). Der konkrete Typ des VStackim Körper oben ist tatsächlich

VStack<TupleView<(Text, Image)>>

Wenn wir später beschließen, dem Stapel eine Ansicht hinzuzufügen, ändert sich sein konkreter Typ. Wenn wir nach dem ersten einen zweiten Text hinzufügen, erhalten wir

VStack<TupleView<(Text, Text, Image)>>    

Selbst wenn wir eine geringfügige Änderung vornehmen, die so subtil ist wie das Hinzufügen eines Abstandshalters zwischen Text und Bild, ändert sich der Typ des Stapels:

VStack<TupleView<(Text, _ModifiedContent<Spacer, _FrameLayout>, Image)>>

Von dem, was ich sagen kann, das ist , der Grund , warum Apple empfiehlt in ihren Tutorials immer zu verwenden some View, die allgemeinste undurchsichtige Art , die alle Ansichten zu erfüllen, als bodyRückgabetyp s‘. Sie können die Implementierung / das Layout Ihrer benutzerdefinierten Ansicht ändern, ohne den Rückgabetyp jedes Mal manuell zu ändern.


Ergänzung:

Wenn Sie ein intuitiveres Verständnis der undurchsichtigen Ergebnistypen erhalten möchten, habe ich kürzlich einen Artikel veröffentlicht, der möglicherweise lesenswert ist:

🔗 Was ist das für ein "in SwiftUI"?

Mischa
quelle
2
Dies. Vielen Dank! Hamishs Antwort war sehr vollständig, aber Ihre sagt mir genau, warum sie in diesen Beispielen verwendet wird.
Chris Marshall
Ich liebe die Idee von "einigen". Gibt es eine Idee, ob die Verwendung von "some" die Kompilierungszeit überhaupt beeinflusst?
Tofu Warrior
@ Mischa also wie macht man generische Ansichten? mit einem Protokoll, das Ansichten und andere Verhaltensweisen enthält?
theMouk
27

Ich denke, was bisher alle Antworten fehlen, ist, dass somees vor allem in so etwas wie einer DSL (domänenspezifischen Sprache) wie SwiftUI oder einer Bibliothek / einem Framework nützlich ist, bei der Benutzer (andere Programmierer) anders sind als Sie.

Sie würden wahrscheinlich nie somein Ihrem normalen App-Code verwenden, außer vielleicht insofern, als es ein generisches Protokoll umschließen kann, damit es als Typ verwendet werden kann (anstatt nur als Typeinschränkung). Was sometut , ist der Compiler hält ein Wissen von dem, was etwas bestimmten Art zu lassen, während einer übergeordneten Typ Fassade davor setzen.

In SwiftUI, wo Sie der Benutzer sind, müssen Sie also nur wissen, dass etwas ein ist some View, während hinter den Kulissen alle Arten von Taschentüchern weitergehen können, von denen Sie abgeschirmt sind. Dieses Objekt ist in der Tat ein sehr spezifischer Typ, aber Sie müssen nie erfahren, was es ist. Im Gegensatz zu einem Protokoll handelt es sich jedoch um einen vollwertigen Typ, da es, wo immer es erscheint, lediglich eine Fassade für einen bestimmten vollwertigen Typ ist.

In einer zukünftigen Version von SwiftUI, in der Sie a erwarten some View, können die Entwickler den zugrunde liegenden Typ dieses bestimmten Objekts ändern. Aber das wird Ihren Code nicht beschädigen, da Ihr Code den zugrunde liegenden Typ überhaupt nicht erwähnt hat.

Somit somemacht in der Tat ein Protokoll eher wie ein Super. Es ist fast ein realer Objekttyp, wenn auch nicht ganz (zum Beispiel kann die Methodendeklaration eines Protokolls kein a zurückgeben some).

Wenn Sie also somefür irgendetwas verwenden würden, wäre es höchstwahrscheinlich, wenn Sie ein DSL oder ein Framework / eine Bibliothek zur Verwendung durch andere schreiben würden und die zugrunde liegenden Typdetails maskieren wollten. Dies würde die Verwendung Ihres Codes für andere einfacher machen und es Ihnen ermöglichen, die Implementierungsdetails zu ändern, ohne deren Code zu beschädigen.

Sie können es jedoch auch in Ihrem eigenen Code verwenden, um eine Region Ihres Codes vor den Implementierungsdetails zu schützen, die in einer anderen Region Ihres Codes vergraben sind.

matt
quelle
23

Das someSchlüsselwort aus Swift 5.1 ( Swift-Evolution-Vorschlag ) wird in Verbindung mit einem Protokoll als Rückgabetyp verwendet.

Xcode 11 Release Notes präsentieren es so:

Funktionen können jetzt ihren konkreten Rückgabetyp ausblenden, indem sie angeben, welchen Protokollen sie entsprechen, anstatt den genauen Rückgabetyp anzugeben:

func makeACollection() -> some Collection {
    return [1, 2, 3]
}

Code, der die Funktion aufruft, kann die Schnittstelle des Protokolls verwenden, hat jedoch keinen Einblick in den zugrunde liegenden Typ. ( SE-0244 , 40538331)

Im obigen Beispiel müssen Sie nicht angeben, dass Sie eine zurückgeben werden Array. Auf diese Weise können Sie sogar einen generischen Typ zurückgeben, der nur dem entspricht Collection.


Beachten Sie auch diesen möglichen Fehler, mit dem Sie möglicherweise konfrontiert werden:

Einige Rückgabetypen sind nur in iOS 13.0.0 oder höher verfügbar

Dies bedeutet, dass Sie die Verfügbarkeit verwenden sollten, um dies someunter iOS 12 und früher zu vermeiden :

@available(iOS 13.0, *)
func makeACollection() -> some Collection {
    ...
}
Cœur
quelle
1
Vielen Dank für diese gezielte Antwort und das Compiler-Problem in Xcode 11 Beta
Brainray
1
Sie sollten die Verfügbarkeit verwenden, um dies someunter iOS 12 und früher zu vermeiden . Solange Sie dies tun, sollte es Ihnen gut gehen. Das Problem ist nur, dass der Compiler Sie nicht davor warnt.
Matt
2
Wie Sie bereits betont haben, erklärt die kurze Beschreibung von Apple alles: Funktionen können jetzt ihren konkreten Rückgabetyp ausblenden, indem sie angeben, welchen Protokollen sie entsprechen, anstatt den genauen Rückgabetyp anzugeben. Und dann kann der Code, der die Funktion aufruft, die Protokollschnittstelle verwenden. Ordentlich und noch mehr.
Fattie
Dies (Ausblenden des konkreten Rückgabetyps) ist bereits ohne Verwendung des Schlüsselworts "some" möglich. Es erklärt nicht den Effekt des Hinzufügens von "some" zur Methodensignatur.
Vince O'Sullivan
@ VinceO'Sullivan Es ist nicht möglich, das someSchlüsselwort in diesem angegebenen Codebeispiel in Swift 5.0 oder Swift 4.2 zu entfernen . Fehler wird sein: " Protokoll 'Sammlung' kann nur als generische Einschränkung verwendet werden, da es Selbst- oder zugehörige Typanforderungen hat "
Cœur
2

'einige' bedeutet undurchsichtigen Typ. In SwiftUI wird View als Protokoll deklariert

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol View {

    /// The type of view representing the body of this view.
    ///
    /// When you create a custom view, Swift infers this type from your
    /// implementation of the required `body` property.
    associatedtype Body : View

    /// Declares the content and behavior of this view.
    var body: Self.Body { get }
}

Wenn Sie Ihre Ansicht als Struktur erstellen, entsprechen Sie dem Ansichtsprotokoll und teilen mit, dass der var body etwas zurückgibt, das das Ansichtsprotokoll bestätigt. Es ist wie eine generische Protokollabstraktion, bei der Sie den konkreten Typ nicht definieren müssen.

varunrathi28
quelle
2

Ich werde versuchen , dies mit sehr einfachen Beispiel aus der Praxis zu beantworten (was das ist ein undurchsichtiges Ergebnistyp über)

Angenommen, Sie haben ein Protokoll mit zugeordnetem Typ und zwei Strukturen, die es implementieren:

protocol ProtocolWithAssociatedType {
    associatedtype SomeType
}

struct First: ProtocolWithAssociatedType {
    typealias SomeType = Int
}

struct Second: ProtocolWithAssociatedType {
    typealias SomeType = String
}

Vor Swift 5.1 ist Folgendes aufgrund eines ProtocolWithAssociatedType can only be used as a generic constraintFehlers unzulässig :

func create() -> ProtocolWithAssociatedType {
    return First()
}

Aber in Swift 5.1 ist das in Ordnung ( somehinzugefügt):

func create() -> some ProtocolWithAssociatedType {
    return First()
}

Oben ist die praktische Verwendung aufgeführt, die in SwiftUI häufig für verwendet wird some View.

Es gibt jedoch eine wichtige Einschränkung: Der zurückgegebene Typ muss zur Kompilierungszeit bekannt sein, sodass die folgenden Schritte nicht funktionieren und Function declares an opaque return type, but the return statements in its body do not have matching underlying typesFehler verursachen:

func create() -> some ProtocolWithAssociatedType {
    if (1...2).randomElement() == 1 {
        return First()
    } else {
        return Second()
    }
}
tzaloga
quelle
0

Ein einfacher Anwendungsfall, der mir in den Sinn kommt, ist das Schreiben allgemeiner Funktionen für numerische Typen.

/// Adds one to any decimal type
func addOne<Value: FloatingPoint>(_ x: Value) -> some FloatingPoint {
    x + 1
}

// Variables will be assigned 'some FloatingPoint' type
let double = addOne(Double.pi) // 4.141592653589793
let float = addOne(Float.pi) // 4.141593

// Still get all of the required attributes/functions by the FloatingPoint protocol
double.squareRoot() // 2.035090330572526
float.squareRoot() // 2.03509

// Be careful, however, not to combine 2 'some FloatingPoint' variables
double + double // OK 
//double + float // error
Artem Ilyumzhinov
quelle