Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Error Handling in Swift 2.0 + Update README #220

Closed
3 tasks done
Jeehut opened this issue Dec 2, 2015 · 5 comments
Closed
3 tasks done

Support Error Handling in Swift 2.0 + Update README #220

Jeehut opened this issue Dec 2, 2015 · 5 comments

Comments

@Jeehut
Copy link
Contributor

Jeehut commented Dec 2, 2015

I just read this in the README:

// Note: Swift currently doesn't have exceptions.
//       Only Objective-C code can raise exceptions
//       that Nimble will catch.

This is not true any more. Swift 2.0 introduced Exceptions and Error handling in general – alongside guard and defer statements to make this easier. There are three things to clear out as far as I can see:

  • Check if Code needs update (I'm guessing 'yes' cause Nimble seems to be catching exceptions)
  • Update Code to support Swift native Error Handling
  • Update README to reflect the changes (if any)
@jeffh
Copy link
Member

jeffh commented Dec 8, 2015

Thanks for filing an issue @Dschee.

It definitely needs to be more clarification. But Swift's error handling is separate from Objective-C's exception handling.

// swift does not catch this exception
do {
    NSException(name: "hello", reason: "world", userInfo: nil).raise()
} catch error {
    // ...
}

While it's good practice to throw exceptions to indicate programmer error, various systems do throw exceptions (eg - CoreData). Also testing throwing of exceptions is useful to test in a library.

Nimble also does handle unexpected swift errors. Also there's a throwError matcher if you expect an error to be thrown.

@Jeehut
Copy link
Contributor Author

Jeehut commented Jan 17, 2016

Thanks for the clarification @jeffh, but I'm not sure I understood all you wanted to say correctly. Does the throwError matcher also match against Swift exceptions? Or is what you want to say that Swift still doesn't have exceptions but instead "throws errors"?

In any case, I didn't see the throwError method before, thanks for that, I think that is what I was asking for. But I still think the note from the README I quoted is clear. At least when I read it I didn't even look for another way of catching errors thrown by the Swift native way cause I thought Nimble is outdated.

Maybe we should change it to something like:

Note: Swifts exception handling works different than Objective-C's. It doesn't catch exceptions raised by NSException. Use the matcher throwError instead if you want to test against exceptions thrown the Swift way.

Or do I still misunderstand?

@jeffh
Copy link
Member

jeffh commented Jan 18, 2016

Sadly, there's limited vocabulary for these definitions. Yes, Swift doesn't have exceptions, but it has syntactic sugar around errors. Hence the differing names of throwError and raiseException.

Exceptions are generally associated to its common implementations:

  • Exceptions need to find their handlers when thrown (aka - find the closest catch / finally block).
    • This implies an extra runtime cost when exceptions are thrown
  • Exceptions use extra storage for handlers + stack metadata
    • Which means having a CPU cache miss when exceptions occur because it's stored in distant memory addresses (0-cost exceptions implementation)
    • Or always incur a small overhead for being in a try / catch for cleaning local memory addresses, if that's used (naive exception implementations)
  • Many implementations capture stack traces when thrown
    • Capture stack information requires more runtime calls and address to function lookups

All of which make exceptions are slower than return values. That's why I'm afraid of using the term exception for Swift's error handling model. It's technically incorrect, even though it might be more common to say it's an exception.

In comparison, errors are more accepted as a data structure that is returned by a function. A perfect example is to see how the go programming language handles errors:

// go code
func stuff() error {
    f, err := os.Open("filename.ext") // Open can fail
    if err != nil {
        // handle error: 98% time we return the error to our caller
        return err
    }
    // do more stuff
}

Obviously this is annoying to write an if case for every error. Especially since the common case is for a function to return its error back to the caller. So swift has syntax that converts code to what go programmers would normally write in functions:

// swift code
func stuff() throws {
    let f = try open("filename.ext")
    // do more stuff
}

Which is much more concise. When handling errors, swift uses syntax similar to how developers are familiar with exceptions:

do {
    try stuff()
} catch let error {
    // log error
}

But that can be a simple conversion to an if statement in compiled code (like go's):

let error = try stuff() // fake: pretending `try` returns the error
if error != nil {
    // log error
}

Of course, it's useful to quote a more authoritative source about swift's implementation. A note under the "Handling Errors" section of the Swift Language Guide that says:

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.

This is the reason why Swift always refers them to errors, and not exceptions.


All that as a long-winded way to say:

  • Objective-C has errors (NSError) and exceptions (NSException)
  • Swift only has errors (ErrorType)

And with the above explanation, you can say:

  • Errors don't suffer the performance cost of exceptions
  • Swift makes errors look like exceptions

I hope that was a bit clearer 😉.

@Jeehut
Copy link
Contributor Author

Jeehut commented Jan 18, 2016

@jeffh Thank you very much for that very detailed explanation. I realize now that I had fallen into the trap to assume I'm dealing with real exception handling when using do, try and catch – I simply had never heard of "catching an error" before, so I assumed the catch must be there for exceptions. Sorry for that misconception on my side. From my perspective, this issue can be closed now.

But in case you care about people who could fall into the same trap as I did, you might want to alter the README to state something like this (providing your great explanation above):

Note: Swift currently doesn't have exceptions (see also #220). Only Objective-C code can raise exceptions that Nimble will catch.

jeffh added a commit that referenced this issue Jan 19, 2016
Which explains more in detail about the differences between exceptions and errors.
@jeffh
Copy link
Member

jeffh commented Jan 19, 2016

Done, thanks for the feedback @Dschee. 👍

@jeffh jeffh closed this as completed Jan 19, 2016
Megal pushed a commit to Megal/Nimble that referenced this issue Jul 31, 2019
Which explains more in detail about the differences between exceptions and errors.
phatblat pushed a commit to phatblat/Nimble that referenced this issue May 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants