I have a suggestion to allow a guard return statement. There are times when you unwrap an optional, and if there is no value, you may throw an error, and then return the value. For example:
let value: Int? = 0
guard let value else {
throw SomeError.unavailable
}
return value
If the syntax allowed a guard return, you could instead simplify the above to:
guard return value else {
throw SomeError.unavailable
}
I would be happy with this as well. It would be great to have both options - the guard's else condition does allow you to add some additional logic though, eg logging prior to throwing. However, when you don't need additional logic, your suggestion would be preferable.
I used the guard here to illustrate the proposal. But you are right though - you could do this. You could also shorten your example to:
if let expression {
return expression
}
throw SomeError.unavailable
However, using a different example:
if let value = someFunction() {
return value
}
throw SomeError.unavailable
Now you need to declare and set value just to return this. This also applies if reading an optional property on some type, eg if let value = expression?.value
It would be nice to just return it without needing to set it first:
If we're exploring ideas for this, here's what I use all the time:
infix operator ?!: NilCoalescingPrecedence
public func ?! <T>(lhs: T?, rhs: @autoclosure () -> Error) throws -> T {
if let value = lhs {
return value
}
throw rhs()
}
This is one of three operators I use for dealing with optionals:
?? → use LHS if it's there, return RHS otherwise ?! → use LHS if it's there, throw RHS otherwise !! → use LHS if it's there, crash with RHS otherwise (aka the "unwrap or die" operator)
I know !! was discussed a lot in the earlier days (maybe it was even implemented in very earlier versions of Swift?) and was apparently controversial.
In any case, when you lay them out together like that, it makes it so clear, and so compelling that they should be standardised (i.e. included in the standard library).
func yeet(_ error: Error) throws -> Never { throw error }
return try value ?? yeet(SomeError())
But it gave me a compile error about using Never in that position (link). I thought the whole point of Never is that it can replace a value of any type?
I was somewhat indifferent to this when this topic started - and I've seen similar if not identical suggestions in years gone by - but for whatever reason it stuck in my mind these last few days, and I've realised that I would use this a lot if it were available. I really do write a lot of code which is basically:
guard let value = maybeFoo() else {
// Logging here etc.
throw SomeError()
}
return value
Or variations thereof (e.g. using try?).
There's no universal shorthand for this today - ?? / ?! / !! work in some cases, but not if there's more to be done in the error case before returning / throwing.
I think there's some aspects that need to be fleshed out clearly - e.g. what does the example below do - but I'm upbeat on this.
func grabFoo() -> Foo? {
guard return maybeFoo() else {
// Logging here etc.
return nil
}
}
i.e. does it consider a nil from maybeFoo to pass the guard (so the else body is actually unreachable) since the grabFoo return type is optional?
Yes it's shorter. however it breaks the "meaning" of guard in my opinion.
Guards could be written with ifs but use have this word to separate the requirement validation from the actual implementation. I also see guards as lines that can be removed "without impacting" the implementation (of course the fonction is then different but only in the requirement not in the general algorithm)
Having guard return value... mixes again implementation from requirement validation, it removes 1 line of code but introduces more clunky code