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 bareexcept:
clause will catchSystemExit
andKeyboardInterrupt
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, useexcept Exception:
(bare except is equivalent toexcept 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:
It is not necessary to list all errors a function can throw, only a
throws
keyword is needed.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 athrows
keyword can be viewed as the function returning anEither
-object, with one being the original return type, and the other being anErrorType
value.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
Any Way to Iterate a Tuple in Swift
Why Is There No Universal Base Class in Swift
Can Associated Values and Raw Values Coexist in Swift Enumeration
Access Input from Uialertcontroller
Hide Navigation Bar Without Losing Swipe Back Gesture in Swiftui
Swift Protocol Error: 'Weak' Cannot Be Applied to Non-Class Type
Swiftui - Navigation Bar Button Not Clickable After Sheet Has Been Presented
Convert Between Decimal, Binary and Hexadecimal in Swift
Swift Write/Save/Move a Document File to Icloud Drive
Init(Start:End:)' Is Deprecated: It Will Be Removed in Swift 3. Use the '..<' Operator
Show Am/Pm in Capitals in Swift
How to Check If a Property Value Exists in Array of Objects in Swift
Why Non Optional Any Can Hold Nil