[Pitch] try/catch - Expose error for try? expressions by allowing catch after the function

Currently, we have the option to handle throwing functions by converting their errors to nil when the function throws:

try? someThrowingFunction()
// or
let results = try? someThrowingFunction()

Pitch

My pitch is to allow catch to be declared after a throwing function with a preceding try? keyword to expose any resulting error:

try? someThrowingFunction() catch {
    print("Caught error: \(error)")
}
guard try? someThrowingFunction() else catch {
    print("Caught error: \(error)")
    return
}

When the throwing function has a return value, the following would apply:

let results = try? someThrowingFunction() catch {
    print("Caught error: \(error)")
}
guard let results = try? someThrowingFunction() else catch {
    print("Caught error: \(error)")
    return
}

The current functional equivalents to the preceding is:

do { try someThrowingFunction() } catch {
    print("Caught error: \(error)")
}
do { try someThrowingFunction() } catch {
    print("Caught error: \(error)")
    return
}

When the throwing function has a return value:

var results: ResultsType?
do { results = try someThrowingFunction() } catch {
    print("Caught error: \(error)")
}
var results: ResultsType?
do { results = try someThrowingFunction() } catch {
    print("Caught error: \(error)")
}
guard let results = results else { return }
4 Likes

I like the guards, I was looking for something exactly like this (in an attempt to reduce a pyramid of do/catch).

Not sure if I'm in love with the try? catch syntax, but I like the guard version, possibly without the question mark:

guard let result = try throwingFunction() else catch {
    print("Caught error: \(error)")
    return
}
7 Likes

This syntax won't work because if the function throws an error, then results won't be bound and can't be accessed below this block.

1 Like

You don't need to use var and Optional in this case:

  let results: ResultsType
  do {
    results = try someThrowingFunction()
  } catch {
    print("Caught error: \(error)")
    return
  }
1 Like

results isn’t available outside of the do block. And that, perplexingly enough, will not do.

I like the pitch.

I support this pitch. do/catch blocks are probably one of my least favorite part about Swift's error throwing. As long as the do/catch block syntax remains supported so it don't break existing APIs, I don't see any downsides to adding this syntactic sugar that will make Swift less redundant and more readable.

I really like the pitch especially the guard example. The current do / catch syntax adds a lot of noise for me. It is nice to use do catch for a batch of calls that have try but for a single try call I really would like your guard approach.

It's a bit confusing though, it looks like throwingFunction is optional. And what if it is?

2 Likes

Good point.

Could you describe the reason for preferring try? instead of try ? The semantics seem to be closer to the existing semantics of try with the change being that catch is now allowed in more places, rather than try? (which discards the error).

3 Likes