You're right; I don't know what it was that was breaking for me.
I can see how someone would see it that way, but it's not an objective observation. Everybody wants the equivalent of this operator, and everybody wants a default—for unwrapping. This should not be conflated with error remapping. The standard library should include both, independently, which would support harmonious usage:
try await { try await stub(nil).unwrapped } ?! StubError.error
(Or, if autoclosures worked…)
try await stub(nil).unwrapped ?! StubError.error
Optional.unwrapped
is not new.
Optional.unwrapped
public extension Optional {
/// Represents that an `Optional` was `nil`.
struct UnwrapError: Error & Equatable {
public init() { }
}
/// - Throws: [`UnwrapError` when `nil`.](https://forums.swift.org/t/unwrap-or-throw-make-the-safe-choice-easier/14453/7)
/// - Note: Useful for emulating `break`, with `map`, `forEach`, etc.
var unwrapped: Wrapped {
get throws {
switch self {
case let wrapped?: return wrapped
case nil: throw UnwrapError()
}
}
}
}
/// Map one `Error` to another upon failure, disregarding the original.
public func ?! <Success>(
_ success: () async throws -> Success,
_ error: @autoclosure () -> some Error
) async throws -> Success {
try await Result(catching: success)
.mapError { _ in error() }
.get()
}
public extension Result where Failure == Error {
init(catching success: () async throws -> Success) async {
do {
self = .success(try await success())
} catch {
self = .failure(error)
}
}
}
Using Result
for this is not necessary, but it's illustrative.