Try, Try! & Try? What's the Difference, and When to Use Each

try, try! & try? what’s the difference, and when to use each?

Updated for Swift 5.1

Assume the following throwing function:

enum ThrowableError: Error {

case badError(howBad: Int)
}

func doSomething(everythingIsFine: Bool = false) throws -> String {

if everythingIsFine {
return "Everything is ok"
} else {
throw ThrowableError.badError(howBad: 4)
}
}

try

You have 2 options when you try calling a function that may throw.

You can take responsibility of handling errors by surrounding your call within a do-catch block:

do {
let result = try doSomething()
}
catch ThrowableError.badError(let howBad) {
// Here you know about the error
// Feel free to handle or to re-throw

// 1. Handle
print("Bad Error (How Bad Level: \(howBad)")

// 2. Re-throw
throw ThrowableError.badError(howBad: howBad)
}

Or just try calling the function, and pass the error along to the next caller in the call chain:

func doSomeOtherThing() throws -> Void {    
// Not within a do-catch block.
// Any errors will be re-thrown to callers.
let result = try doSomething()
}

try!

What happens when you try to access an implicitly unwrapped optional with a nil inside it? Yes, true, the app will CRASH!
Same goes with try! it basically ignores the error chain, and declares a “do or die” situation. If the called function didn’t throw any errors, everything goes fine. But if it failed and threw an error, your application will simply crash.

let result = try! doSomething() // if an error was thrown, CRASH!

try?

A new keyword that was introduced in Xcode 7 beta 6. It returns an optional that unwraps successful values, and catches error by returning nil.

if let result = try? doSomething() {
// doSomething succeeded, and result is unwrapped.
} else {
// Ouch, doSomething() threw an error.
}

Or we can use guard:

guard let result = try? doSomething() else {
// Ouch, doSomething() threw an error.
}
// doSomething succeeded, and result is unwrapped.

One final note here, by using try? note that you’re discarding the error that took place, as it’s translated to a nil.
Use try? when you’re focusing more on successes and failure, not on why things failed.

Using Coalescing Operator ??

You can use the coalescing operator ?? with try? to provide a default value incase of failure:

let result = (try? doSomething()) ?? "Default Value"
print(result) // Default Value

difference between try and except before the function & in the function

The try in your first example is not evaluated when the function is being called, only when the function is first being defined, at which point there are no exceptions being raised.

It's not until you actually call the function with arguments where the key doesn't exist in the given dictionary that an exception is actually raised and that try/except clause isn't being evaluated then.

Your second example is the correct way to do what you're trying to do:

In [4]: def chiz(dict, key): 
...: try:
...: return dict[key]
...: except:
...: print("Something went wrong")
...:

In [5]: chiz(user_01, "weight")
Something went wrong

That being said, reconsider that bare except clause. It is equivalent to saying except BaseException: which is probably not what you want and is against PEP8 recommendations. In this case, you should probably be using except KeyError: or, at the most broad, except Exception:.

When catching exceptions, mention specific exceptions whenever possible instead of using a bare except: clause. A bare except: clause will catch SystemExit and KeyboardInterrupt exceptions, making it harder to interrupt a program with Control-C, and can disguise other problems. If you want to catch all exceptions that signal program errors, use except Exception: (bare except is equivalent to except BaseException:).
A good rule of thumb is to limit use of bare 'except' clauses to two cases:

  • If the exception handler will be printing out or logging the traceback; at least the user will be aware that an error has occurred.
  • If the code needs to do some cleanup work, but then lets the exception propagate upwards with raise. try...finally can be a better way to handle this case.

And from PEP463:

any situation where you catch an exception you don't expect to catch is an unnecessary bug magnet.

What is the difference between Try and Either?

I covered the relationship between Try, Either, and Option in this answer. The highlights from there regarding the relationship between Try and Either are summarized below:

Try[A] is isomorphic to Either[Throwable, A]. In other words you can treat a Try as an Either with a left type of Throwable, and you can treat any Either that has a left type of Throwable as a Try. It is conventional to use Left for failures and Right for successes.

Of course, you can also use Either more broadly, not only in situations with missing or exceptional values. There are other situations where Either can help express the semantics of a simple union type (where value is one of two types).

Semantically, you might use Try to indicate that the operation might fail. You might similarly use Either in such a situation, especially if your "error" type is something other than Throwable (e.g. Either[ErrorType, SuccessType]). And then you might also use Either when you are operating over a union type (e.g. Either[PossibleType1, PossibleType2]).

Since Scala 2.12, the standard library does include the conversions from Either to Try or from Try to Either. For earlier versions, it is pretty simple to enrich Try, and Either as needed:

object TryEitherConversions {
implicit class EitherToTry[L <: Throwable, R](val e: Either[L, R]) extends AnyVal {
def toTry: Try[R] = e.fold(Failure(_), Success(_))
}

implicit class TryToEither[T](val t: Try[T]) extends AnyVal {
def toEither: Either[Throwable, T] =
t.map(Right(_)).recover(Left(_)).get
}
}

This would allow you to do:

import TryEitherConversions._

//Try to Either
Try(1).toEither //Either[Throwable, Int] = Right(1)
Try("foo".toInt).toEither //Either[Throwable, Int] = Left(java.lang.NumberFormatException)

//Either to Try
Right[Throwable, Int](1).toTry //Success(1)
Left[Throwable, Int](new Exception).toTry //Failure(java.lang.Exception)

what is the difference between TRY....CATCH and TRY.....Finally?

Eveything that is enclosed in your finally block is ensured to be executed, and it could be useful in these 2 concrete cases at least :

  • Sometimes you decide to call return in the middle of your try block and get back to the caller : finally ease the process of releasing ressources here, you don't have to write some specific code to go directly to the end of your method.
  • Sometimes you want to let an exception go up (by not catching it) and maybe being caught at a higher level (because it is not the appropriate place to handle it properly for example). Again finally ensures your resources are released and the exception continue its path.

Maybe you can see finally as a tool helping developpers to do things they're obliged to do with less effort. On the other side, catch is dedicated to handle errors.

Both keywords are dedicated to flow control, but they don't have the same purpose and they can be used one without each other (and often are!). It depends on your needs.

Difference between try-finally and try-catch

These are two different things:

  • The catch block is only executed if an exception is thrown in the try block.
  • The finally block is executed always after the try(-catch) block, if an exception is thrown or not.

In your example you haven't shown the third possible construct:

try {
// try to execute this statements...
}
catch( SpecificException e ) {
// if a specific exception was thrown, handle it here
}
// ... more catches for specific exceptions can come here
catch( Exception e ) {
// if a more general exception was thrown, handle it here
}
finally {
// here you can clean things up afterwards
}

And, like @codeca says in his comment, there is no way to access the exception inside the finally block, because the finally block is executed even if there is no exception.

Of course you could declare a variable that holds the exception outside of your block and assign a value inside the catch block. Afterwards you can access this variable inside your finally block.

Throwable throwable = null;
try {
// do some stuff
}
catch( Throwable e ) {
throwable = e;
}
finally {
if( throwable != null ) {
// handle it
}
}

Single try meaning in Swift

In addition to the cases you mentioned, you can call try at
top-level code. Here is a simple self-contained example:

// main.swift:
enum MyError : Error {
case failed
}

func foo() throws -> Int {
throw MyError.failed
}

defer { print("Good bye.") }

let x = try foo()
print(x)

You can compile and run this as a Xcode "Command Line Project"
or directly from the command line:


$ swiftc main.swift

$ ./main
Good bye.
Fatal error: Error raised at top level: main.MyError.failed: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.74.1/src/swift/stdlib/public/core/ErrorType.swift, line 187
Illegal instruction: 4

The failed try in the top-level code causes the program to
terminate with an error message. Deferred statement (if present) will be executed however.

This is slightly different from using a forced try! statement,
which causes the program to abort as well, but immediately, without executing deferred statements. (This can be relevant if deferred
statements are used to clean-up resources, e.g. remove temporary files).


The error message originates from ErrorType.swift, line 187:

/// Invoked by the compiler when code at top level throws an uncaught error.
@_inlineable // FIXME(sil-serialize-all)
@_silgen_name("swift_errorInMain")
public func _errorInMain(_ error: Error) {
fatalError("Error raised at top level: \(String(reflecting: error))")
}

(also observed in Non Exhaustive List When Handling Errors Inside a Class Function in Swift).

Apparently the top-level code behaves as if embedded in a
do-catch block:

do {
func foo() throws -> Int {
throw NSError(domain: "foo", code: 0, userInfo: nil)
}

defer { print("Good bye.") }

let x = try foo()
} catch {
fatalError("Error raised at top level: \(String(reflecting: error))")
}

What is the difference between `try` and `&.` (safe navigation operator) in Ruby

&. works like #try!, not #try.

And here is description of #try! (from documentation):

Same as #try, but will raise a NoMethodError exception if the receiving is not nil and does not implemented the tried method.

So basically it saves you from calling a method on nil, but if an object is presented it will try to call its method as usual.

The quote is from Rails Documentation, and so it's important to emphasize
that Ruby does not provide #try; it's provided by Rails, or more accurately ActiveSupport. The safe navigation operator (&.) however, is a language feature presented in Ruby 2.3.0.

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



Related Topics



Leave a reply



Submit