Skip to content

Instantly share code, notes, and snippets.

@JadenGeller
Last active November 7, 2015 14:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JadenGeller/902fbb8ae1a86c2d5f7c to your computer and use it in GitHub Desktop.
Save JadenGeller/902fbb8ae1a86c2d5f7c to your computer and use it in GitHub Desktop.
Try/Catch Exception Handling in Swift
// A very basic implementation of try/catch blocks in Swift
// Note that this is not thread safe
// Also note that this does not support nested try/catch statements
// We will start with an example
func test(x: Bool) {
try{
println("HI")
if x { throw() }
println("BYE")
}
catch{ e in
println("BAD STUFF")
}
}
test(true) // -> "HI"
// "BAD STUFF"
test(false) // -> "HI"
// "BYE"
// Woot, it works as expected!
// Let's try one more example!
func divide(a: Int, b: Int) -> Int {
if b == 0 { throw() }
return a / b
}
divide(6,2) // -> 3
divide(6,0) // CRASH!
try{ divide(6,0) } // (nothing)
// That's right, throw() kills the program but try{ throw() } is safely handled.
// Now lets see how it works:
// Global variables. Yes, I know, gross! But I wanted to write this quickly.
// To make this thread-safe, make these thread specific globals.
// Note that this can be implemented without global variables of any kind,
// but we lose our beautiful, easy to use syntax.
// Stores the status of our try/catch statement
var code: Int? = nil
// Stores the location to jump to on a throw() call
var jump = UnsafeMutablePointer<Int32>(malloc(UInt(sizeof(jmp_buf))))
// Boring. Note we could expand this to allow
// us to add further info such as a description
// string. This would require us to store that
// in some global variable just like code and jump.
struct Exception {
let code: Int
}
/*
* Recieves a closure to be tried. If a throw() is called
* within that closure, it will jump back to the Jump Position
* (skipping the execution of the rest of the closure) with a
* nonzero value for our code variable. Thus, f will not be
* executed a second time.
*/
func try(f: () -> ()) {
code = Int(setjmp(jump)) // Jump Position
if code == 0 {
// Haven't yet tried
f()
}
}
/*
* Optional function to be called after a try statement
* that allows you to handle any errors that occured.
* An exception is passed in that can be used to identify
* what error occured in our try block.
*/
func catch(f: Exception -> ()) {
if let thrownCode = code {
if thrownCode != 0 {
// Exception occured
f(Exception(code: thrownCode))
}
code = nil // Prevents two successive catch blocks
}
else {
fatalError("A catch statement must follow some try statement")
}
}
// Convenience function
func throw() {
throw(-1)
}
// Jumps back to the try block skipping execution of the rest of the closure
func throw(errorCode: Int) {
if code == 0 {
// Fail out of try block
longjmp(jump, Int32(errorCode))
}
else{
// We are outside of a try block
fatalError("An exception was thrown with error code \(errorCode)")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment