Methods that throw and return Never

Whenever I write a function that always throws, I want to call it with throw instead of try:

// Assuming this function exists
func missingValue() throws -> Never {
    throw MissingValueError()
}

// Call site
try missingValue()

// I wish I could write instead:
throw missingValue()

I think the problem with try, in this context, is that only the compiler is aware that the method returns Never:

guard let value else {
    // It looks like `missingValue` could NOT throw.
    // Aren't we missing a `return` or something?
    try missingValue()
}
value.doSomething()

With throw, the code would be perfectly clear:

guard let value else {
    throw missingValue() // No brainer
}
value.doSomething()

Naming the method with a throw prefix can help, but it does not look good:

try throwMissingValue()

Oh, I did not mention that this mostly happens when calling an initializer for the thrown error would be inconvenient (for reasons), or when the method that throws wants to throw one error type or another. This is not very frequent, but I've met this need several times in a few years.


EDIT :thinking: A method that returns an error would fit the bill - I have to sharpen my argument, showing why someone sometimes absolutely needs a throws -> Never method.

1 Like

If missingValue() always throws an error and never returns, is there a reason why it can't be defined as:

func missingValue2() -> MissingValueErrro {
  MissingValueError()
}

?

If I squint, I can almost make your version with throw missingValue() make sense to me, except that I expect missingValue() to produce a value of Error type, not necessarily throw itself (i.e., just what I've written in missingValue2()).

1 Like

Yes, I'm also wondering why simply returning an error is not good enough.

I'm right now working on such case (that's why I asked). It happens that the error factory method can itself throw:

// () throws -> Never
func missingValue() throws -> Never {
    if Bool.random() { throw OtherError() }
    throw MissingValueError()
}

try missingValue()

If I turn it into a method that returns an error, I have:

// () throws -> any Error
func missingValue() throws -> any Error {
    if Bool.random() { throw OtherError() }
    return MissingValueError()
}

throw try missingValue()

So, yes, I can finally write throw, but actually it's throw try :neutral_face:.

There are use cases for this, of course. But in my particular case, it happens that the "error produced while producing the error" is not interesting for the end user. This is a case where () throws -> Never remains superior to () throws -> any Error.


EDIT. If I return any Error, I can catch the inner error:

// () -> any Error
func missingValue() -> any Error {
    do {
        if Bool.random() { throw OtherError() }
        return MissingValueError()
    } catch {
        return error
    }
}

throw missingValue()

I guess this is perfectly fine :slight_smile:

Thank you @Jumhyn

In such a case perhaps it makes sense for the wrapper to catch the internally thrown error to maintain the () -> any Error signature?

func missingValue() -> any Error {
  do {
    if Bool.random() { throw OtherError() }
  } catch {
    return error
  }
  return MissingValueError()
}

throw missingValue()

Yes, thank you for helping me on this one :+1: :heart:

1 Like