Was sind die Unterschiede zwischen Würfen und erneuten Würfen in Swift?

81

Nachdem ich nach einigen Referenzen gesucht hatte, um es herauszufinden, konnte ich leider keine nützliche und einfache Beschreibung zum Verständnis der Unterschiede zwischen throwsund finden rethrows. Es ist etwas verwirrend, wenn man versucht zu verstehen, wie wir sie verwenden sollen.

Ich würde erwähnen, dass ich mit dem -default- throwsmit seiner einfachsten Form zur Verbreitung eines Fehlers wie folgt vertraut bin :

enum CustomError: Error {
    case potato
    case tomato
}

func throwCustomError(_ string: String) throws {
    if string.lowercased().trimmingCharacters(in: .whitespaces) == "potato" {
        throw CustomError.potato
    }

    if string.lowercased().trimmingCharacters(in: .whitespaces) == "tomato" {
        throw CustomError.tomato
    }
}

do {
    try throwCustomError("potato")
} catch let error as CustomError {
    switch error {
    case .potato:
        print("potatos catched") // potatos catched
    case .tomato:
        print("tomato catched")
    }
}

So weit so gut, aber das Problem entsteht, wenn:

func throwCustomError(function:(String) throws -> ()) throws {
    try function("throws string")
}

func rethrowCustomError(function:(String) throws -> ()) rethrows {
    try function("rethrows string")
}

rethrowCustomError { string in
    print(string) // rethrows string
}

try throwCustomError { string in
    print(string) // throws string
}

Was ich bisher weiß, ist beim Aufrufen einer Funktion, dass throwssie im tryGegensatz zu der von a verarbeitet werden muss rethrows. Na und?! Welcher Logik sollten wir folgen, wenn wir uns für throwsoder entscheiden rethrows?

Ahmad F.
quelle

Antworten:

184

Aus "Erklärungen" im Swift-Buch:

Funktionen und Methoden neu werfen

Eine Funktion oder Methode kann mit dem rethrowsSchlüsselwort deklariert werden, um anzuzeigen, dass sie nur dann einen Fehler auslöst, wenn einer ihrer Funktionsparameter einen Fehler auslöst. Diese Funktionen und Methoden werden als Umwerffunktionen und Umwerfverfahren bezeichnet . Funktionen und Methoden zum erneuten Werfen müssen mindestens einen Parameter für die Wurffunktion enthalten.

Ein typisches Beispiel ist die mapMethode:

public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]

Wenn mapes mit einer nicht auslösenden Transformation aufgerufen wird, löst es selbst keinen Fehler aus und kann ohne aufgerufen werden try:

// Example 1:

let a = [1, 2, 3]

func f1(n: Int) -> Int {
    return n * n
}

let a1 = a.map(f1)

Aber wenn mapmit einem Wurfverschluss gerufen wird, dann kann selbst werfen und muss gerufen werden mit try:

// Example 2:

let a = [1, 2, 3]
enum CustomError: Error {
    case illegalArgument
}

func f2(n: Int) throws -> Int {
    guard n >= 0 else {
        throw CustomError.illegalArgument
    }
    return n*n
}


do {
    let a2 = try a.map(f2)
} catch {
    // ...
}
  • Wenn mapals throwsstattdessen deklariert rethrowswürde, müssten Sie es trysogar in Beispiel 1 aufrufen , was "unpraktisch" ist und den Code unnötig aufbläht.
  • Wenn mapohne deklariert würde, könnten throws/rethrowsSie es nicht mit einem Wurfverschluss wie in Beispiel 2 aufrufen.

Das gleiche gilt für andere Methoden der Swift - Standardbibliothek , die Funktionsparameter nehmen: filter(), index(where:), forEach()und viele viele mehr.

In deinem Fall,

func throwCustomError(function:(String) throws -> ()) throws

bezeichnet eine Funktion, die einen Fehler auslösen kann, selbst wenn sie mit einem nicht auslösenden Argument aufgerufen wird, wohingegen

func rethrowCustomError(function:(String) throws -> ()) rethrows

bezeichnet eine Funktion, die nur dann einen Fehler auslöst, wenn sie mit einem auslösenden Argument aufgerufen wird.

Grob gesagt rethrowsgilt dies für Funktionen, die Fehler nicht "alleine" auslösen, sondern nur Fehler aus ihren Funktionsparametern "weiterleiten".

Martin R.
quelle
36
Der letzte Satz ist golden!
Klaas
1
@Honey: Der letzte Satz in der Antwort ist, wie ich es zusammenfassen würde.
Martin R
Ja das scheint besser. Wäre es richtig zu sagen, dass rethrows nur mit Verschlüssen verwendet werden, außer dass sie nicht benötigt werden?
Honig
3
@Honey: Ich verstehe nicht ganz, was du meinst. rethrowswird nur mit Funktionen verwendet, die Funktionsparameter annehmen, die möglicherweise ausgelöst werden.
Martin R
Wie wäre die Syntax, wenn es beides tun würde? 🤔🤔 Wäre es "Rethrows Throws"?
Kautsya Kanu
14

Nur um etwas zusammen mit Martins Antwort hinzuzufügen. Eine Nicht-Wurffunktion mit derselben Signatur wie eine Wurffunktion wird als eine sub-typeder Wurffunktionen betrachtet. Aus diesem Grund können Rethrows bestimmen, um welche es sich handelt, und nur dann, trywenn der func-Parameter ebenfalls ausgelöst wird, aber immer noch dieselbe Funktionssignatur akzeptiert, die nicht ausgelöst wird. Es ist eine bequeme Möglichkeit, nur einen do try-Block verwenden zu müssen, wenn der func-Parameter ausgelöst wird, aber der andere Code in der Funktion gibt keinen Fehler aus.

JustinM
quelle