Overview:
I have a function which accepts Error? as a parameter.
I would like to check if there is an error I would like to handle the error, if there is no error I would like to do some processing.
Question
What is the preferred approach (it need not be one of the 3 that I have mentioned).?
I have mentioned below 3 approaches that I could think of, was wondering if there was a better way to handle it.
Code
func f1(error: Error?) {
//Gaurd is more expressive of the intent but end up having to force unwrap
guard error == nil else {
print("Error: \(error!)") //Forced unwrapping
return
}
print("start processing")
}
func f2(error: Error?) {
//Intent is not as clear as the guard statement, but no force unwrapping
if let error = error {
print("Error: \(error)")
return
}
print("start processing")
}
func f3(error: Error?) {
switch error {
case .none:
print("start processing")
//This is ok but all the processing code would be embeded with in the case
case .some(let errorExists):
print("Error: \(errorExists)")
}
}
I believe the preferred and 'swifty' approach is not digging into the optional – either the first or second, further it is a matter of style. Force-unwrapping in the body of the guard statement is absolutely safe since you know it isn't nil, but not necessary:
Thanks @anthonylatsis, I understand from your reply that the 1st or the 2nd approach is fine and that force unwrapping is fine with in body of the guard statement.
preferred and ‘swifty’ approach is not digging into the optional
Did you mean unwrapping when you mentioned "digging" ?
This shouldn't be preferred at all in this situation. Furthermore you ignore that error is still optional and the compiler will emit a warning when you pass an optional value to print. (Or have you missed the force unwrap? But I still discourage you from writing code like this even though it's true that you're safe to force unwrap there.)
I think the cleanest solution here is to unwrap it and return from the main function or proceed otherwise.
func f2(error: Error?) {
if let error = error {
return print("Error: \(error)")
}
print("start processing")
}
So f2 is the way to go here. I also would consider f3 as good enough but that depends on the nested scopes that will follow in the real wold use case (avoid pyramid of doom).
I would too go with f2, but you know, passing an optional to print never hurt a fly. I didn't say it is preferred: I mentioned it isn't necessary (unwrapping to print).
Personally I hate force unwarps even if in theory you can use them in a 'safe' manner, but this is just a wrong habit IMO.
Here is one example of a library we have to use in our project, but I'm so freaking scared to touch it because internally they abuse force unwraps to the max.
Presently I am using f2, the problem that I encountered with guard is that accidentally if I added another condition then I might be in trouble.
Example:
guard error == nil,
a == 10 else { //assuming a is also a parameter and needed to check before processing
print("Error: \(error!)") //Here error was nil and it crashed
return
}
I think it's a great question and one that doesn't really have an answer I personally prefer f2, or for something slightly more complex than an Optional (e.g. Result) I go with f3 if I need to handle more than one case.
Also in the f3 case I'd go with the "error" case first, to follow the "bail out early" style of writing code.