2017-04-09 47 views
21

Po wyszukaniu pewnych odniesień, aby to rozgryźć, niestety, nie mogłem znaleźć użytecznego i prostego opisu zrozumienia różnic między throws i rethrows. To trochę zagmatwane, gdy próbujemy zrozumieć, jak powinniśmy z nich korzystać.Jakie są różnice między rzutami a ponownymi rzutami w Swift?

chciałbym wspomnieć, że jestem trochę zaznajomiony z -default- throws z najprostszej postaci propagacji błędu, co następuje:

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

Jak na razie dobrze, ale problem powstaje, kiedy:

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 
} 

co wiem do tej pory podczas wywoływania funkcji, która throws musi być obsługiwane przez try, w przeciwieństwie do rethrows. Więc co?! Jaką logikę powinniśmy przestrzegać, decydując się na użycie throws lub rethrows?

Odpowiedz

57

Od "Declarations" w Swift książki:

Ponowne generowanie funkcji i metod

Funkcja lub metoda może być zadeklarowana z rethrows hasła do wskazują, że zgłasza błąd tylko wtedy, gdy jeden z to funkcja Parametry powodują błąd. Te funkcje i metody są znane pod nazwą . Funkcje ponownego rzucania i metody ponownego rzucania. Funkcje Rethrowing i metody muszą mieć co najmniej jeden parametr funkcji throwing.

Typowym przykładem jest metoda map:

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

Jeśli map nazywa się non-rzucanie przekształcać, nie rzucać błąd się i można nazwać bez try:

// Example 1: 

let a = [1, 2, 3] 

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

let a1 = a.map(f1) 

Ale jeśli map zostanie wywołany z zamknięciem rzucania, sam może rzucić d musi być wywoływana z 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 { 
    // ... 
} 
  • Jeśli map zostały zadeklarowane jako throws zamiast rethrows to byś trzeba nazwać z try nawet w przykładzie 1, która jest „niewygodne” i bloats kod niepotrzebne.
  • Jeśli map został zadeklarowany bez throws/rethrows, to nie można go było wywołać za pomocą zamknięcia do rzucania, jak w przykładzie 2.

To samo odnosi się do innych metod z Swift biblioteki standardowej które odbywają parametry funkcyjne: filter(), index(where:), forEach() i wiele, wiele więcej.

W twoim przypadku,

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

oznacza funkcję, która może wyrzucić błąd, nawet jeśli nazwie z niebędącego rzucanie argumentów, natomiast

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

oznacza funkcję, która generuje błąd tylko jeśli zostanie wywołany z argumentem rzutu o wartości .

Z grubsza rzecz biorąc, rethrows jest do funkcji, które nie rzucają błędów „na własną rękę”, ale tylko „do przodu” błędy z ich funkcją parametrów.

+1

Świetna odpowiedź. Dzięki. – Darko

+3

Ostatnie zdanie jest złote! – Klaas

+1

, więc domyślam się podsumować, 'rethrow' kiedy * może * chcesz rzucić. "rzucaj", gdy chcesz ** ograniczyć do zawsze ** rzucania – Honey

7

Wystarczy dodać coś razem z odpowiedzią Martina. Funkcja bez rzucania z tym samym podpisem co funkcja rzutu jest uważana za sub-type funkcji rzutu. Dlatego rethrows może określić, który to jest, i wymaga tylko try, gdy funkparam także rzuca, ale nadal akceptuje tę samą sygnaturę funkcji, która nie rzuca. Jest to wygodny sposób, aby użyć tylko bloku try, gdy funk param rzuca, ale drugi kod w funkcji nie powoduje błędu.