How to pass an Error up the stack trace in Swift

2019-01-12 12:40发布

问题:

In java, if one method throws an error, the method that calls it can pass it on to the next method.

public void foo() throws Exception {
     throw new Exception();
}
public void bar() throws Exception {
     foo();
}
public static void main(String args[]) {
     try {
         bar();
     }
     catch(Exception e) {
         System.out.println("Error");
     }
}

I am writing an app in swift and would like to do the same thing. Is this possible? If it is not possible what are some other possible solutions? My original function that makes the call has this structure.

func convert(name: String) throws -> String {

}

回答1:

Referring to Swift - Error Handling Documentation, you should:

1- Create your custom error type, by declaring enum which conforms to Error Protocol:

enum CustomError: Error {
    case error01
}

2- Declaring foo() as throwable function:

func foo() throws {
    throw CustomError.error01
}

3- Declaring bar() as throwable function:

func bar() throws {
    try foo()
}

Note that although bar() is throwable (throws), it does not contain throw, why? because it calls foo() (which is also a function that throws an error) with a try means that the throwing will -implicitly- goes to foo().

To make it more clear:

4- Implement test() function (Do-Catch):

func test() {
    do {
        try bar()
    } catch {
        print("\(error) has been caught!")
    }
}

5- Calling test() function:

test() // error01 has been caught!

As you can see, bar() automatically throws error, which is referring to foo() function error throwing.



回答2:

A Swift function can call a throwing function and pass an error up to the caller, but

  • The function itself must be marked with throws, and
  • the throwing function has to be called with try.

Example:

func foo() throws {
    print("in foo")
    throw NSError(domain: "mydomain", code: 123, userInfo: nil)
}

func bar() throws -> String {
    print("in bar")
    try foo()
    return "bar"
}

do {
    let result = try bar()
    print("result:", result)
} catch {
    print(error.localizedDescription)
}

Output:

in bar
in foo
The operation couldn’t be completed. (mydomain error 123.)

If try foo() fails then bar() returns immediately, propagating the error thrown by foo() to its caller. In other words, try foo() inside a throwing function is equivalent to

do {
    try foo()
} catch let error {
    throw error
}


回答3:

Swift handles the propagation of errors, and does not have an exceptions mechanism. In Swift, a called function can pass an error it encounters to the context that it was called from. But in spite of using the keyword throw it's not actually throwing an exception. It's just passing the error out through a channel that is distinct from the function's return value.

I suppose that routine, the caller, could then interpret the error in the function it has called as an error itself, and pass a related error to it's caller, but in general there is no mechanism in Swift to allow you to jump up through stack frames when an erroneous condition is encountered.



回答4:

In Swift, errors are represented by values of types that conform to the Error protocol. This empty protocol indicates that a type can be used for error handling.

In Swift Only throwing functions can propagate errors. Any errors thrown inside a nonthrowing function must be handled inside the function.

Throwing an error lets you indicate that something unexpected happened and the normal flow of execution can’t continue. You use a throw statement to throw an error.

There are four ways to handle errors in Swift.

  • You can propagate the error from a function to the code that calls that function,
  • handle the error using a do-catch statement,
  • handle the error as an optional value, or
  • assert that the error will not occur.

    Unlike exception handling in many languages—including Objective-C—error handling in Swift does not involve unwinding the call stack

While Swift errors are used in a similar way to Java's Checked exceptions, they are not quite the same thing.

Error handling in Swift resembles exception handling in other languages, with the use of the try, catch and throw keywords. Unlike exception handling in many languages—including Objective-C—error handling in Swift does not involve unwinding the call stack, a process that can be computationally expensive. As such, the performance characteristics of a throw statement are comparable to those of a return statement.

see Swift Error Handling