Interesting. Why is do throw notation required in case of that "do" statement being inside of a closure whilst it is optional when the do statement is inside of a function?
However, I met the second problem… Combine unwrap and typed throws…
struct AError: Error {}
let a: Int?
func b() throws(AError) -> Int { 1 }
do throws(AError) {
// I want to make it inline
let c = try a ?? b() // error: thrown expression type 'any Error' cannot be converted to error type 'AError'
} catch {}
?? is defined as a function that does not recognize typed throw (yet): func ?? <T>(T?, @autoclosure () throws -> T) rethrows -> T // FIXME: typed throws
arguments passed to @autoclosure parameters needs to be transformed into the form of typed-throws closures, but now they are treated just like normal throwing closures.
The best way I can find to express your idea is using if-expressions:
do throws(AError) {
let c = if let a { a } else { try b() }
} catch {}
As Mr. Wu pointed out, ?? operator doesn’t currently support typed throws. Depending on your use case, Mr. Wu’s answer is decent, but I’d like to suggest an alternative (a very rough idea):
extension Optional {
func unwrap(
defaultValue: Wrapped
) -> Wrapped {
switch self {
case .some(let v): v
case .none: defaultValue
}
}
}
struct AError: Error {}
let a: Int? = nil
func b() throws(AError) -> Int { 1 }
do throws(AError) {
let c = try a.unwrap(defaultValue: b())
} catch {}
// Or
do throws(AError) {
let c = try { () throws(AError) -> Int in
switch a {
case .some(let a): a
case .none: try b()
}
}()
} catch {}
Errr, yes you could... technically... as short circuiting is not inherently bound to the operator ?? itself (just its concrete func implementation). As for the operator – it's merely a convention, similar to that used for other operators like ||. However, if to follow the spirit of that convention I'd use a different symbol that doesn't suggest short-circuiting behaviour... for example optional | defaultValue() if to reuse / hijack | from bitwise ops.
Having said that, in real code I'd probably take the path of least resistance to workaround this limitation and use the code suggested by @CrystDragon :
// TODO: check this in a year or three to see if typed throws work better with autoclosures
// test1: let c = try a ?? b()
// test2: let c = a ?? (try b())
// until then will be using this workaround:
let c = if let a { a } else { try b() }