В чем разница между бросками и повторными бросками в Swift?

После поиска некоторых ссылок, чтобы разобраться в этом, - к сожалению - я не смог найти полезного - и простого - описания различий между 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?


person Ahmad F    schedule 09.04.2017    source источник


Ответы (2)


Из «Объявления» книги 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 предназначен для функций, которые не генерируют ошибки «сами по себе», а только «пересылают» ошибки из своих параметров функции.

person Martin R    schedule 09.04.2017
comment
Последнее предложение золотое! - person Klaas; 10.08.2017
comment
@Honey: Последнее предложение в ответе - это то, как я бы это резюмировал. - person Martin R; 22.08.2017
comment
Да, кажется, лучше. Было бы правильно сказать, что rethrow используются только с замыканиями, кроме того, что они не нужны? - person Honey; 22.08.2017
comment
@Honey: Я не совсем понимаю, что вы имеете в виду. rethrows используется только с функциями, которые принимают параметры функции, которые могут выдавать. - person Martin R; 22.08.2017
comment
Каким был бы синтаксис, если бы он делал и то, и другое ?? ???????? Были бы броски ретроунов ?? - person Kautsya Kanu; 03.08.2020
comment
@KautsyaKanu: Только throws. - person Martin R; 03.08.2020

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

person JustinM    schedule 09.04.2017