I want to revive a discussion that was started 7 years ago in this thread. I find myself often writing long do
blocks when calling into throwing APIs, effectively nesting much more code than actually needed, leading to the catch
clause being very far away from the actual code that could have caused its entrance. For example:
func randomMovies(genre: Genre, count: Int) -> [Movie] {
do {
let movies = try Database.loadMovies(byGenre: genre)
// no throwing API calls from here on...
guard !movies.isEmpty else { return [] }
var randomMovies: [Movie] = []
for _ in 0..<count {
randomMovies.append(movies.randomElement()!)
}
return randomMovies
} catch {
return []
}
}
Likewise, I might want to provide different catch clauses for different throwing functions. Currently I have to provide nested do-catch
statements to achieve that, which leads to less readable and less concise code very quickly.
I'm proposing to introduce a catch
clause added to if
or guard
statements that is only usable and is even required if the if/guard condition includes a try
call. For example, the above code could be written like this instead:
func randomMovies(genre: Genre, count: Int) -> [Movie] {
guard let movies = try Database.loadMovies(byGenre: genre)
catch { return [] }
guard !movies.isEmpty else { return [] }
var randomMovies: [Movie] = []
for _ in 0..<count {
randomMovies.append(movies.randomElement()!)
}
return randomMovies
}
This could also keep support for Optional types with else
like suggested by Chris Lattner here:
I could also imagine that we keep the questionmark for try?
, I've not thought about the syntax extensively. But I really want the advantages of a guard let
for throwing APIs:
- Multiple of them with their own
catch
blocks like I can provide multipleguard
s withelse
blocks - Avoid nesting for code that follows the
guard
as there's no throwing API involved there anymore
What do you think? Or was there any reason this was not further elaborated?