Які відмінності між кидками та перекиданнями у Свіфта?


83

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

Відповіді:


185

З "Декларацій" у книзі Свіфта:

Відновлення функцій і методів

Функцію або метод можна оголосити за допомогою 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це для функцій, які не видають помилки "самі по собі", а лише "пересилають" помилки зі своїх параметрів функції.


37
Останнє речення золоте!
Клаас,

1
@Honey: Останнє речення у відповіді - це те, як я б це підсумував.
Martin R

Так, це здається краще. Чи правильно було б сказати, що rethrows використовуються лише з закриттями, крім того, що вони не потрібні?
Мед,

3
@Honey: Я не повністю розумію те, що ви маєте на увазі. rethrowsвикористовується лише з функціями, які приймають параметри функції, які можуть викликати.
Martin R

Яким би був синтаксис, якщо він виконує обидва ?? 🤔🤔 Це було б "перекидання кидків" ??
Kautsya Kanu

14

Просто щоб додати щось разом із відповіддю Мартіна. Функція не метання з тим самим сигнатуром, що функція метання, вважається sub-typeфункцією метання. Ось чому перевиробництво може визначити, який це, і вимагає лише tryтоді, коли параметр func також кидає, але все одно приймає той самий підпис функції, який не кидає. Це зручний спосіб використовувати блок do try лише тоді, коли параметр func видає, але інший код у функції не видає помилки.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.