Sự khác biệt giữa ném và ném lại trong Swift là gì?


81

Sau khi tìm kiếm một số tài liệu tham khảo để tìm ra nó, thật không may, tôi không thể tìm thấy mô tả hữu ích và đơn giản- về việc hiểu sự khác biệt giữa throwsrethrows. Thật là khó hiểu khi cố gắng hiểu cách chúng ta nên sử dụng chúng.

Tôi muốn đề cập rằng tôi khá quen thuộc với -default- throwsvới dạng đơn giản nhất của nó để truyền lỗi, như sau:

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

Cho đến nay rất tốt, nhưng vấn đề phát sinh khi:

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
}

những gì tôi biết cho đến nay là khi gọi một hàm, throwsnó phải được xử lý bởi a try, không giống như rethrows. Vậy thì sao?! Logic mà chúng ta nên tuân theo khi quyết định sử dụng throwshoặc là rethrowsgì?

Câu trả lời:


184

Từ "Tuyên bố" trong sách Swift:

Rethrowing Functions and Method

Một hàm hoặc phương thức có thể được khai báo với rethrowstừ khóa để chỉ ra rằng nó chỉ tạo ra lỗi khi một trong các tham số hàm của nó ném ra lỗi. Các hàm và phương thức này được gọi là các hàmphương thức ném lại . Các hàm và phương thức ném lại phải có ít nhất một tham số hàm ném.

Một ví dụ điển hình là mapphương pháp:

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

Nếu mapđược gọi với một biến đổi không ném, nó không tự tạo ra lỗi và có thể được gọi mà không cần try:

// Example 1:

let a = [1, 2, 3]

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

let a1 = a.map(f1)

Nhưng nếu mapđược gọi với một bao đóng ném thì bản thân nó có thể ném và phải được gọi với 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 {
    // ...
}
  • Nếu mapđược khai báo là throwsthay vì rethrowsthì bạn sẽ phải gọi nó bằng tryngay cả trong ví dụ 1, điều này là "bất tiện" và làm cồng kềnh mã không cần thiết.
  • Nếu mapđược khai báo mà không có throws/rethrowsthì bạn không thể gọi nó với một bao đóng ném như trong ví dụ 2.

Điều này cũng đúng đối với các phương pháp khác từ Swift thư viện chuẩn mà lấy thông số chức năng: filter(), index(where:), forEach()và nhiều nhiều hơn nữa.

Trong trường hợp của bạn,

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

biểu thị một hàm có thể tạo ra một lỗi, ngay cả khi được gọi với một đối số không ném ra, trong khi

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

biểu thị một hàm ném lỗi chỉ khi được gọi với đối số ném.

Nói một cách đại khái, rethrowslà đối với các hàm không tự tạo ra lỗi "của riêng chúng", mà chỉ có lỗi "chuyển tiếp" từ các tham số hàm của chúng.


36
Câu cuối cùng là vàng!
Klaas

1
@Honey: Câu cuối cùng trong câu trả lời là cách tôi sẽ tóm tắt nó.
Martin R

Vâng, điều đó có vẻ tốt hơn. Có đúng không khi nói rethrows chỉ được sử dụng với các bao đóng, ngoài việc chúng không cần thiết?
Honey

3
@Honey: Tôi không hoàn toàn hiểu ý bạn. rethrowschỉ được sử dụng với các hàm nhận tham số hàm có thể ném.
Martin R

Cú pháp sẽ như thế nào nếu nó làm cả hai ?? 🤔🤔 Nó sẽ là "ném ném lại" ??
Kautsya Kanu

14

Chỉ để thêm một cái gì đó cùng với câu trả lời của Martin. Một hàm không ném có cùng ký hiệu với một hàm ném được coi là một sub-typetrong các hàm ném. Đó là lý do tại sao các lần ném lại có thể xác định nó là cái nào và chỉ yêu cầu trykhi tham số func cũng ném, nhưng vẫn chấp nhận cùng một chữ ký hàm không ném. Đó là một cách thuận tiện để chỉ phải sử dụng khối do try khi thông số func ném, nhưng mã khác trong hàm không gây ra lỗi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.