Potentially surprising behavior around rethrows checking

I recently encountered a couple of interesting behaviors around rethrows checking.

I tried writing something like:

func f(_ k: (() throws -> ())? = nil) rethrows {
    if let k_ = k {
        try k_() // error: call can throw, but the error is not handled; a function declared 'rethrows' may only throw if its parameter does
    }
}

Ok, the compiler can't reason that k_'s throwing-ness is connected to k's throwing-ness (not an unreasonable restriction, since reasoning about arbitrary types apart from Optional could be messy/impossible).

Since that's an error, I tried making it a no-op by default to work around it.

func f(_ k: () throws -> () = { }) rethrows {
  try k()
}


let x = f()
// error: call can throw but is not marked with 'try'
// note: call is to 'rethrows' function, but a defaulted argument function can throw

To some extent, I do understand why we'd want to not treat the default argument type as something different from the parameter type.

I don't really have a question, I just figured these were a couple of interesting edge cases, so I'd share them.

The latter is tracked as SR-1534. I agree it would be nice to solve, tricky though it may be.

2 Likes

I agree that these are frustrating edge cases, and I don’t have anything to add there. But if you’re looking for a way to rethrow based on an optional function, you can use the k?() notation:

func f(_ k: (() throws -> ())? = nil) rethrows {
    try k?()
}
2 Likes

This issue came up just three days ago on another thread: