Differencebetween Swift 2.0 Do-Try-Catch and Regular Java/C#/C++ Exceptions

What is the difference between Swift 2.0 do-try-catch and regular Java/C#/C++ exceptions

There are 3 major differences I have found:

  1. It is not necessary to list all errors a function can throw, only a throws keyword is needed.

  2. There is no significant slowdown when using these errors, while Java and other languages need to construct an Exception object and unwind the stack. In Swift a throws keyword can be viewed as the function returning an Either-object, with one being the original return type, and the other being an ErrorType value.

  3. In Swift all errors need to be handled or declared to be thrown, it is impossible to get an error from a method that does not state it is throwing an error. (in Java terms, all errors are "checked exceptions")

Is it correct to call an error created with Swift's keyword throw an exception ?

I would argue that why it is called error handling has nothing to do with the call stack. This is corroborated by the fact that based on the definitions of both words, the call stack is irrelevant. I believe that the purpose of the quote that you have included in your question is merely to provide a distinction between error-handling in Swift and exception-handling in other languages, disregarding the differences in names.

To identify the distinction in terms of non-swift languages, a common description is that it is an error not to handle an exception. Thus an error and an exception are two unique entities.

Now in Swift, it seems that they have tried to completely get rid of the word "exception" in favor of just using the term error. Thus an error can be dealt with, and if not, the program crashes. This is likely because it is not as important what the actual crash is called, and what is more important is what caused it, in this case an "error".

In terms of usage in the iOS world, I have little experience in this, but I would assume that even though to call it "exception handling" is not technically correct, most Swift programmers would know what you are talking about and probably not correct you (or even think to correct you).

Overall, I think it is mostly a matter of semantics, and not what constitutes an "error" and what constitutes an "exception".

Edit

I should clarify that I mean that the difference between errors and exceptions within iOS/Swift is not just semantics. Exceptions are what are thrown when illegal things happen, and errors can be created to allow you to prevent these messages from being shown/your program crashing. In this respect they are completely different things.

My point is that among different coding languages the term "error" (in Swift) and "exception" (in Java for example) are basically the same, just with different names.

For example, I could try and deal with an error named "ArrayError" (stupid name I know, it's just for an example) where as in Java I could be trying to catch IndexOutOfBoundsException. Both of these objects are both thrown and caught, and thus I am drawing a comparison between the two highlighting the differences in naming conventions in Swift vs other languages.

But no, errors and exceptions are not technically the same thing, even in Swift.

Swift: repetitive try catch code

The block parameter must be marked as throwing:

func tryCatch<T>(block: () throws -> T) -> T? {
do {
return try block()
} catch let error as NSError {
print(error)
}
return nil
}

Using a generic function instead of Any makes the type
casts redundant.

As @Hamish correctly said, you still have to call the throwing function
with try, e.g.

let player = tryCatch {
try AVAudioPlayer(contentsOf: selectedSoundURL!)
}

What is the advantage of using try {} catch {} versus if {} else {}

I'd use the try/catch block when the normal path through the code should proceed without error unless there are truly some exceptional conditions -- like the server being down, your credentials being expired or incorrect. I wouldn't necessarily use it to handle non-exceptional errors -- say like the current user not being in the correct role. That is, when you can reasonably expect and handle an error that is not an exceptional condition, I think you should do your checks.

In the case that you've described -- setting up and performing a query, a try/catch block is an excellent way to handle it as you normally expect the query to succeed. On the other hand, you'll probably want to check that the contents of result are what you expect with control flow logic rather than just attempting to use data that may not be valid for your purpose.

One thing that you want to look out for is sloppy use of try/catch. Try/catch shouldn't be used to protect yourself from bad programming -- the "I don't know what will happen if I do this so I'm going to wrap it in a try/catch and hope for the best" kind of programming. Typically you'll want to restrict the kinds of exceptions you catch to those that are not related to the code itself (server down, bad credentials, etc.) so that you can find and fix errors that are code related (null pointers, etc.).

Any real-life nested exceptions that are easy to intentionally cause in C#?

You could use Task.Run(() => throw new Exception()); for example. This will throw an AggregateException which will contain the exception as an inner exception.
Invoking things that throw exceptions via reflection will also cause a TargetInvocationException to be thrown containing the actual exception as an inner exception.

Using the XmlSerializer to deserialize an invalid XML file usally produces a more deeply nested error hierarchy if I recall correctly.

For example the following program will throw an exception three "levels" deep:

public class MyClass
{
[XmlElement("Element")]
int Element { get; set; }

}
class Program
{
static void Main(string[] args)
{
string xml = "<Element>String</Element>";
XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
serializer.Deserialize(new StringReader(xml));
}
}

But by far the simplest solution of course is to throw your own nested exception.

How to decide between using if/else vs try/catch?

You should never use try/catch for flow control.

Generating an exception is an extremely expensive action. If/else is much faster and cleaner.

Swift do-try-catch syntax

There are two important points to the Swift 2 error handling model: exhaustiveness and resiliency. Together, they boil down to your do/catch statement needing to catch every possible error, not just the ones you know you can throw.

Notice that you don't declare what types of errors a function can throw, only whether it throws at all. It's a zero-one-infinity sort of problem: as someone defining a function for others (including your future self) to use, you don't want to have to make every client of your function adapt to every change in the implementation of your function, including what errors it can throw. You want code that calls your function to be resilient to such change.

Because your function can't say what kind of errors it throws (or might throw in the future), the catch blocks that catch it errors don't know what types of errors it might throw. So, in addition to handling the error types you know about, you need to handle the ones you don't with a universal catch statement -- that way if your function changes the set of errors it throws in the future, callers will still catch its errors.

do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
} catch let error {
print(error.localizedDescription)
}

But let's not stop there. Think about this resilience idea some more. The way you've designed your sandwich, you have to describe errors in every place where you use them. That means that whenever you change the set of error cases, you have to change every place that uses them... not very fun.

The idea behind defining your own error types is to let you centralize things like that. You could define a description method for your errors:

extension SandwichError: CustomStringConvertible {
var description: String {
switch self {
case NotMe: return "Not me error"
case DoItYourself: return "Try sudo"
}
}
}

And then your error handling code can ask your error type to describe itself -- now every place where you handle errors can use the same code, and handle possible future error cases, too.

do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch let error as SandwichError {
print(error.description)
} catch {
print("i dunno")
}

This also paves the way for error types (or extensions on them) to support other ways of reporting errors -- for example, you could have an extension on your error type that knows how to present a UIAlertController for reporting the error to an iOS user.

How can I break from a try/catch block without throwing an exception in Java

The proper way to do it is probably to break down the method by putting the try-catch block in a separate method, and use a return statement:

public void someMethod() {
try {
...
if (condition)
return;
...
} catch (SomeException e) {
...
}
}

If the code involves lots of local variables, you may also consider using a break from a labeled block, as suggested by Stephen C:

label: try {
...
if (condition)
break label;
...
} catch (SomeException e) {
...
}


Related Topics



Leave a reply



Submit