Подтвердить что ты не робот

Каковы различия между бросками и повторениями в Свифт?

После поиска некоторых ссылок, чтобы понять это, к сожалению, я не мог найти полезного и простого описания о понимании различий между throws и rethrows. Это пугает, когда мы пытаемся понять, как мы должны их использовать.

Я бы упомянул, что я довольно хорошо знаком с -default- throws с его простейшей формой для распространения ошибки следующим образом:

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")
    }
}

Пока все хорошо, но проблема возникает, когда:

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
}

что я знаю до сих пор, когда вы вызываете функцию, которая throws должна обрабатываться try, в отличие от rethrows. И что?! Что мы должны придерживаться при выборе throws или rethrows?

4b9b3361

Ответ 1

От "Объявления" в книге Swift:

Повторные функции и методы

Функцию или метод можно объявить с помощью ключевого слова rethrows. указывают, что он выдает ошибку только в том случае, если одна из ее функций параметры выдают ошибку. Эти функции и методы известны как реверсивные функции и методы реорганизации. Повторные функции и методы должны иметь по крайней мере один параметр функции бросания.

Типичным примером является метод map:

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

Если map вызывается с не-бросающим преобразованием, он не бросает сама ошибка и может быть вызвана без try:

// Example 1:

let a = [1, 2, 3]

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

let a1 = a.map(f1)

Но если map вызывается с закрытием бросания, то сам может бросить и должен быть вызван с помощью 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 {
    // ...
}
  • Если map были объявлены как throws вместо rethrows, тогда вы нужно называть его try даже в примере 1, который "неудобен" и раздувает код ненужным.
  • Если map были объявлены без throws/rethrows, тогда вы не смогли бы вызовите его с закрытием бросания, как в примере 2.

То же самое верно для других методов из стандартной библиотеки Swift которые принимают параметры функции: filter(), index(where:), forEach() и многие другие.

В вашем случае

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

обозначает функцию, которая может вызывать ошибку, даже если она вызвана аргумент без бросания, тогда как

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

обозначает функцию, которая выдает ошибку, только если вызывается с метательный аргумент.

Грубо говоря, rethrows - для функций, которые не бросают ошибки "сами по себе", но только "передовые" ошибки от их функции параметры.

Ответ 2

Просто добавьте что-нибудь вместе с Мартином. Функция не бросания с той же сигнатурой, что и функция бросания, считается функцией sub-type функции throwing. Вот почему rethrows может определить, какой из них есть, и требует только try, когда параметр func также выдает, но все же принимает ту же сигнатуру функции, которая не выбрасывает. Это удобный способ использовать блок try, только когда функция func param бросает, но другой код в функции не выдает ошибки.