It would be convenient to have do-try-catch block in "early-exit style". Like:
do-guard let encrypted = try cypher.encrypt(bytes) catch let e {
print("Encryption error:", e)
return // Thank Adrian for reminding
}
// Massive code
send(encrypted)
I personally came to the conclusion that guard catch would be confusing with guard else. What we should do instead is just allow a guard like syntax instead of the do body with 1-n comma separated list of throwing expressions.
do
let value1 = try getValue(),
let value2 = try getValue(),
try throwingVoidFunction()
catch {
print(error)
return
}
Right.
I actually wrote it as a reply to the pieces of code posted by @xwu and @DevAndArtist, where the content of the error was also ignored (the difference being of course that in their code, the content of the error could be accessed if needed).
I should have written: âIf itâs ok to ignore what the error is, there is also this approach.â
I love the fact that we have try? for some of those cases where you only care about whether there was an error and not about what the error was.
There have been some pitches for this guard catch syntax and I think it would be extremely valuable to swift. For my use case I am using SwiftNio and as recommended I want to return failed futures instead of throwing errors. I want to do something like:
This is not currently possible. I know there have been several threads about this but donât recall all the details of where they ended up.
I have personally encountered situations where this would be very useful. It is an unfortunate wart in Swiftâs error handling syntax that nesting of the happy path is required when you need to catch errors.
The syntax you presented is how I have imagined it. I can also imagine multiple catch blocks in the same way as is supported by do / catch, as long as there is a âcatch allâ at the end of the catch chain. I can also imagine supporting allowing this to be mixed with the current guard / else syntax so we can use things like if let alongside try in guarded expressions.
When an else block is present, I can imagine allowing the âcatch allâ block to be omitted, allowing catching of specific errors while letting the rest flow through to the else block (with no error binding). However, we might want to omit this âfeatureâ because it could lead to unintentional âswallowingâ of errors when boolean guards such as if let are mixed with try in the same guard statement.
I agree. Something like the following example where you have blocks to distinguish between errors or the failed unwrap of the the result of buyFavoriteSnack() would be ideal
var vendingMachine = VendingMachine()
guard let snack = try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine) catch VendingMachineError.invalidSelection {
print("Invalid Selection.")
return
} catch VendingMachineError.outOfStock {
print("Out of Stock.")
return
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
return
} catch {
print("Unexpected error: \(error).")
return
} else {
print("No error, But could not unwrap optional")
return
}
print("mmm I love eating \(snack.name)")
I prefer your previous variant without the try?. Using try? is already a wart in the system and should only be used when the error is not relevant I guess.
Your previous example though is beautiful:
guard let a = try something() catch { ⌠} else { ⌠}
Yeah, thatâs what I have in mind. Iâd change your example from try? to try though. As written, the code would fail to compile because youâre swallowing the error with an Optional.
Itâs usually not a good idea to have a throwing API return Optional so Iâd look for another way to introduce optionality. A dictionary subscript or optional chain on the result of the throwing API are common ways Swiftâs error handling system bumps into optionals without a throwing API returning an Optional result.
@anandabits that is a good point about how a throwing functions usually should not return an optional value. You even get an error if you use a guard statement with on a non-optional value. So if this type of guard-try-catch functionality was added to Swift the guard keyword in the guard-try-catch statement would have to be a special case or maybe it would be best to use a different keyword all together?
No, I don't think it should use a different keyword. The compiler should know whether else, catch, or both are required by looking at the expressions. If try is used by any of the guarded expressions then catch is required. If unwrapping, pattern matching or boolean conditions are used then an else is required.
@anandabits, to prevent mixing problems this feature could be implemented as a similar, but separate construction.
guard let someData = data else { return nil }
guard let museum = try decoder.decode(Museum.self, from: data) catch let e {
print("Museum decoding error: \(e)")
return nil
}
It's not a big problem actually to repeat the guard multiple times. I do it often even with guard let, because I need to process each condition failure in a different way.
What "mixing problems"? I don't think there are "mixing problems" as long as we don't allow else to catch errors. If we're going to add this feature I think we should do it in a way that integrates nicely with the existing feature, allowing unwrapping, pattern matching, boolean expressions and error handling to be freely intermixed in guard statements.