# \[Pitch\] Optional.orThrow

**URL:** <https://forums.swift.org/t/pitch-optional-orthrow/69995>\
**Category:** Pitches\
**Created:** [February 12, 2024, 9:34pm UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995 "2024-02-12T21:34:35Z")\
**Posts on this page:** 18\
**Page:** 2

<div class="post-metadata">

**Author:** ![pyrtsa](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/pyrtsa/32/740_2.png) [@pyrtsa](https://forums.swift.org/u/pyrtsa)\
**Post date:** [April 9, 2024, 9:04am UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/21 "2024-04-09T09:04:48Z")

</div>

You don't need new overloads to make use of `??` for this in Swift today. It's enough to provide this one extension to `Optional`:

```swift
extension Optional {
    static func `throw`(_ error: some Error) throws -> Self {
        throw error
    }
}

struct NumberError: Error {}

func demo(input: String) throws {
    let int = try Int(input) ?? .throw(NumberError())
}

```

---

<div class="post-metadata">

**Author:** ![Dmitriy\_Ignatyev](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dmitriy_ignatyev/32/11340_2.png) [@Dmitriy\_Ignatyev](https://forums.swift.org/u/Dmitriy_Ignatyev)\
**Post date:** [April 9, 2024, 10:13am UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/22 "2024-04-09T10:13:44Z")

</div>

> [@pyrtsa](#):
>
> It's enough to provide this one extension to `Optional`

Function name `throw` doesn't looks quite clear from the call site:

```swift
let value = value.throw(AppError.unexpectedNilValue)

```

It is felt like `value`-instance has `throw(_:)` method that unconditionally throws an error.  
There is nothing about unwrapping of optional value or conditional flow.

However I'm not a strong proponent of method declared in `Optional` extension. I'm fine with it, but I also believe we should pick a better name for such function if we go this way.

Besides I think we should look for more convenient and short ways to express the idea.

Some metrics form my current project:

- nil coalescing operator is used 569 times
- unwrapOrThrow is used 52 times (not so much at a first look)

I'm sure some kind of `unwrapOrThrow` syntax will be used more often because of the following reasons:

- adoption of `async / await` will make the `Result<>` an order of magnitude less used. In my current codebase result is used 284 times in completion handlers, all of them will be migrated to `async`.
- adoption of typed throws will also decrease `Result<>` usage. There are ~237 DTOs having methods for conversion to domain models + ~60 other methods using the `Result<>` due to lack of typed throws.  
Nevertheless, `Result<>` will also be broadly used along the codebase, e.g. to store or transfer some data that is wrapped by `Result<>`.

Thus looking at my project it is clear for me that `async / await` with combination of `typed throws` will significantly increase the number of throwable functions. Many of these throwable functions need to unwrap optional values.  
Because of that I have a strong feeling that we need something better than method declared in `Optional` extension.

I have come up with idea to explore Swift open source projects for analysis while writing this post.

**[edited]**

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [April 9, 2024, 10:39am UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/23 "2024-04-09T10:39:47Z")

</div>

> [@Dmitriy\_Ignatyev](#):
>
> Function name `throw` doesn't looks quite clear from the call site.  
> There is nothing about unwrapping of optional value in or conditional flow.

That's why there's `??` part in @pyrtsa example.

What does feel off – both "try" and "throw" are used.. normally it's either one or another:

```swift
try expression() // no "visible" throw here
...
throw error // no try here

```

---

<div class="post-metadata">

**Author:** ![pyrtsa](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/pyrtsa/32/740_2.png) [@pyrtsa](https://forums.swift.org/u/pyrtsa)\
**Post date:** [April 9, 2024, 12:12pm UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/24 "2024-04-09T12:12:24Z")

</div>

> [@Dmitriy\_Ignatyev](#):
>
> It is felt like `value`-instance has `throw(_:)` method that unconditionally throws an error.

I'm sorry I think you misread my snippet. I wasn't adding an instance member to `Optional` but a `static func`.

---

<div class="post-metadata">

**Author:** ![Dmitriy\_Ignatyev](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dmitriy_ignatyev/32/11340_2.png) [@Dmitriy\_Ignatyev](https://forums.swift.org/u/Dmitriy_Ignatyev)\
**Post date:** [April 9, 2024, 12:14pm UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/25 "2024-04-09T12:14:54Z")

</div>

> [@pyrtsa](#):
>
> you misread my snippet

You are right, thanks for pointing this out.

---

<div class="post-metadata">

**Author:** ![Dmitriy\_Ignatyev](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dmitriy_ignatyev/32/11340_2.png) [@Dmitriy\_Ignatyev](https://forums.swift.org/u/Dmitriy_Ignatyev)\
**Post date:** [April 9, 2024, 12:56pm UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/26 "2024-04-09T12:56:23Z")

</div>

There is also a related aspect to discuss: should we have a default Error type and an overload for those cases when someone doesn't care about error Type or does't want to create own error?

```swift
extension Optional {
  // allows to specify custom error Type
  public func unwrapOrThrow<E: Error>(_ errorExpression: @autoclosure () -> E) throws(E) -> Wrapped {
    guard let wrapped = self else { throw errorExpression() }
    return wrapped
}

  // will throw default error
  public func unwrapOrThrow() throws(some Error) -> Wrapped {
    guard let wrapped = self else { throw OptionalUnwrappingFailureError() }
    return wrapped
  }
}

struct OptionalUnwrappingFailureError: Error {
  private let errorDescription: String?
  private let typeOfWrapped: Any.Type
}

```

If yes, should we add `file: StaticString = #fileID, line: UInt = #line` arguments to `unwrapOrThrow() throws(some Error)` function. This might be helpful in scenarios when error is propagated to upstream layers and finally logged.

---

<div class="post-metadata">

**Author:** ![Hacksaw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hacksaw/32/32775_2.png) [@Hacksaw](https://forums.swift.org/u/Hacksaw)\
**Post date:** [April 9, 2024, 4:07pm UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/27 "2024-04-09T16:07:55Z")

</div>

I like the Perl naming in this case:

`try x ?? die(SomeError.error)`

where `die` is accompanied by a few friends like `warn`, `carp`, and `croak`, which display messages in different formats, and may or may not end execution.

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [April 10, 2024, 3:24am UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/28 "2024-04-10T03:24:12Z")

</div>

My favourite alternative of making a "crash safe version of !" (which is essentially what this pitch is about) is [unchecked exceptions](https://forums.swift.org/t/testing-fatalerror-and-friends/67317/69). Benefits:

- as with [`x^ alternative`](https://forums.swift.org/t/pitch-optional-orthrow/69995/20) mentioned above, it will support L-value expressions. This is in sharp contrast to anything discussed before in this of previous related pitches.

- compared to `x^` mentioned above will not require a language / compiler change (only the standard library change).

- will require a minimal amount of source changes (textually it would still look like `x!` just behave slightly differently if embedded within `dynamicTry`/`catch` brackets directly or indirectly. Note that this is a strict opt-in (if you don't "activate" this feature by using `dynamicTry`/`catch` there won't be any behavioural changes).

- could be used for other things later on (array subscripts, etc)

---

<div class="post-metadata">

**Author:** ![KeithBauerANZ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/keithbaueranz/32/27897_2.png) [@KeithBauerANZ](https://forums.swift.org/u/KeithBauerANZ)\
**Post date:** [April 10, 2024, 3:32am UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/29 "2024-04-10T03:32:58Z")

</div>

I think what a lot of the "I want this to look like postfix `!`" posts miss from the original, is that actually, the nature of the thrown error is important for many use cases. For example, the original builder pattern example:

```swift
struct XXXBuilder {
    var a: A?
    var b: B?
    var c: C?

    func build() throws(XXXBuilderError) -> XXX {
        try XXX(
            a: a.orThrow(XXXBuilderError.missingA),
            b: b.orThrow(XXXBuilderError.missingB),
            c: c.orThrow(XXXBuilderError.missingC)
        )
    }
}

```

Simply loses too much information if done with something that throws a fixed error type:

```swift
struct XXXBuilder {
    var a: A?
    var b: B?
    var c: C?

    func build() throws(NilOptionalError) -> XXX {
        try XXX(
            a: a.unwrapped,
            b: b.unwrapped,
            c: c.unwrapped
        )
    }
}

```

The information about _which_ field is missing has been lost, and can't be reconstructed without introducing syntax just as verbose as the original.

So whilst I can certainly see having an overload with a default error, to make it more convenient in these cases, I think the version where the error is specified is still critical.

---

<div class="post-metadata">

**Author:** ![jaleel](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jaleel/32/45076_2.png) [@jaleel](https://forums.swift.org/u/jaleel)\
**Post date:** [April 10, 2024, 7:27am UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/30 "2024-04-10T07:27:31Z")

</div>

IMHO throw as an expression will be more suited for Swift and that's what should be brought up. Until that using Result type, simple guard, closure or some helper function feels like enough. Remember there was also discussion about doing guards like

```swift
guard let a else throw Error

```

which could be an option, but also matter of preference though.

Btw

> [@KeithBauerANZ](#):
>
> ```swift
> struct XXXBuilder {
> var a: A?
> var b: B?
> var c: C?
> 
> func build() throws(NilOptionalError) -> XXX {
> try XXX(
> a: a.unwrapped,
> b: b.unwrapped,
> c: c.unwrapped
> )
> }
> }
> 
> ```

builder feels a bit more javaish, when you can directly use `init`

```swift
struct XXX {
    let a: A
    let b: B
    let c: C

    init(a: A?, b: B?, c: C?) throws(NilOptionalError) {
        // whatever checking you prefer here
    }
}

```

but also matter of preference of course. 🙂 (I do get that builder could be used from different places).

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [April 10, 2024, 11:31am UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/31 "2024-04-10T11:31:12Z")

</div>

> [@KeithBauerANZ](#):
>
> I think the version where the error is specified is still critical.

Error information could be preserved...

> **Using your code with unchecked exceptions**
>
> ```swift
> typealias A = Int
> typealias B = Int
> typealias C = Int
> 
> struct XXX {
> var a: A
> var b: B
> var c: C
> }
> 
> struct XXXBuilder {
> var a: A?
> var b: B?
> var c: C?
> 
> func build() -> XXX {
> XXX(
> a: a.unwrap(),
> b: b.unwrap(), // line 22
> c: c.unwrap()
> )
> }
> }
> 
> let xxx = dynamicTry {
> XXXBuilder(a: 1, b: nil, c: 3).build()
> } catch: { error in
> print(error)
> }
> print(xxx)
> 
> ```

produces this output:

> [@](#):
>
> [App.OptionalUnwrapError.unwrappingNil("App/main.swift", 22)]

correctly pinpointing the nature of the error ("OptionalUnwrapError.unwrappingNil") and its exact location ("App/main.swift", 22):

```swift
        XXX(
            a: a.unwrap(),
            b: b.unwrap(), // line 22
            c: c.unwrap()
        )

```

* * *

It is true that if we "upgrade" the vanilla `"!"` to use unchecked exceptions machinery and don't make any changes to the language itself you won't be able adding a descriptive comment about the error:

```swift
let y = x! // no comment / custom error here
x!.mutatingFunc() // ✅ L-value usage possible

let y = x.unwrap(comment: "x") // ✅ comment / custom error possible
x.unwrap(comment: "x").mutatingFunc() // 🛑 L-value usage impossible

```

If both L-value usage and a comment (or a customised error) is needed we'd need a language change, e.g.:

```swift
let y = x! // ✅
let z = x!(comment: "x") // ✅
x!.mutatingFunc() // ✅
x!(comment: "x").mutatingFunc() // ✅

```

* * *

Correction / Addition:

We'd be able abusing subscripts to add a descriptive comment / customised error:

```swift
let y = x! // ✅
let z = x[] // ✅
let t = x[comment: "x"] // ✅
x!.mutatingFunc() // ✅
y[].mutatingFunc() // ✅
t[comment: "x"].mutatingFunc() // ✅

```

> **The code sketch**
>
> ```swift
> protocol HasDefaultValue {
> static var defaultValue: Self { get }
> }
> extension HasDefaultValue where Self: Inittable {
> static var defaultValue: Self { .init() }
> }
> extension Optional where Wrapped: HasDefaultValue {
> // TODO: make some parameters autoclosures for efficiency
> subscript(comment comment: String = "", file: String = #fileID, line: Int = #line) -> Wrapped {
> get {
> guard let self else {
> print("record error, file: \(file), line: \(line), comment: \(comment)")
> return Wrapped.defaultValue
> }
> return self
> }
> set {
> self = newValue
> }
> }
> }
> struct SS: Inittable, HasDefaultValue {
> mutating func mutatingFunc() {}
> }
> var xx: SS?
> xx[comment: ""].mutatingFunc()
> 
> ```

Is it possible using a "subscript" approach with normal try/catch instead of the above unchecked exceptions? To some extent only:

- only for the R-value use cases (throwing subscripts are limited to be get-only at the moment).

- or if this restriction is lifted and we have get/set throwing subscripts in the language.

* * *

As with pretty much any feature there are multiple aspects at hand here, and multiple interpretations of what should be "a goal" and "non goal". Let me write a few:

1. the feature should not require language / compiler change
2. the feature shall use the standard try / catch mechanism
3. the feature should allow adding a descriptive comment (or allow to specify a customised error)
4. the feature shall allow identifying the error source
5. the feature should be a "safe" version of `"!"` usable in all contexts (including L-value).

I noticed that other people value #1 most and #5 least, and personally I'd choose the priorities the opposite way around: #5 on my book is the most important and #2 & #3 (and even #1 if needed) could be sacrificed.

---

<div class="post-metadata">

**Author:** ![KeithBauerANZ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/keithbaueranz/32/27897_2.png) [@KeithBauerANZ](https://forums.swift.org/u/KeithBauerANZ)\
**Post date:** [April 10, 2024, 9:47pm UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/32 "2024-04-10T21:47:15Z")

</div>

> [@tera](#):
>
> 1. the feature should not require language / compiler change
> 2. the feature shall use the standard try / catch mechanism
> 3. the feature should allow adding a descriptive comment (or allow to specify a customised error)
> 4. the feature shall allow identifying the error source
> 5. the feature should be a "safe" version of `"!"` usable in all contexts (including L-value).

OK, so here's an extension of the proposal in the original post, which satisfies all 5:

```swift
public extension Optional {
    func unwrap<E: Error>(
        orThrow error: @autoclosure () -> E
    ) throws(E) -> Wrapped {
        guard let wrapped = self else {
            throw error()
        }
        return wrapped
    }

    struct NilOptionalError: Error {
        public init() {}
    }

    var unwrapped: Wrapped {
        get throws {
            unwrap(orThrow: NilOptionalError())
        }
        set {
            self = newValue
        }
    } 
}

```

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [April 10, 2024, 10:47pm UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/33 "2024-04-10T22:47:29Z")

</div>

About 4 of 5 bullet points above are broken!

- For starters, it doesn't even compile...

> [@](#):
>
> error: 'set' accessor is not allowed on property with 'get' accessor that is 'async' or 'throws'

If we are removing the setter (then we might as well remove the `var unwrapped` as well) – we are losing #5. If we removing "throws" we are losing #2

- file/line/comment/or custom error information would be impossible to add to "var unwrapped" (breaking #3 and #4)

- behaviour of `x! = value` would be different to `try x.unwrapped = value` (if it did compile, I mean). I didn't put that into a bullet point but it would be reasonable to expect "when `!` did trap – this would throw" behaviour.

- `try x.unwrapped.mutatingFunc()` would be impossible to have without dropping #1, breaking #2 and or #5.

---

<div class="post-metadata">

**Author:** ![KeithBauerANZ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/keithbaueranz/32/27897_2.png) [@KeithBauerANZ](https://forums.swift.org/u/KeithBauerANZ)\
**Post date:** [April 10, 2024, 11:07pm UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/34 "2024-04-10T23:07:23Z")

</div>

> [@tera](#):
>
> For starters, it doesn't even compile

I did not expect that!

[swift-evolution/proposals/0310-effectful-readonly-properties.md at main · swiftlang/swift-evolution · GitHub](https://github.com/apple/swift-evolution/blob/main/proposals/0310-effectful-readonly-properties.md#effectful-settable-properties) explains that settable throwing properties were omitted only due to complexity, not for any fundamental reason. Still, obviously that would mean a compiler change to go this way.

Personally, if we're admitting that this feature requires a compiler change, allowing `throw E` as an expression that can take on any type seems the right one. (I absolutely do not care about the "default error" or "lvalue" use-cases, though I certainly understand that others do)

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [April 11, 2024, 1:41am UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/35 "2024-04-11T01:41:01Z")

</div>

Right. Just when we throw "compiler changes" into the mix it opens up questions like "could we compromise less" and "if there were no limits what would the ideal syntax look like and the ideal behaviour be". I doubt that having an incoherent two-face answer of a get-only file/line/comment/error customisable `unwrap()` on one hand and a rudimentary in those regards but writeable `unwrapped` on another (different syntax, different features, different rules, no support of `x! = value` behaviour) makes an ideal feature compared to the syntactic simplicity of `!` (built-in pseudo) operator.

---

<div class="post-metadata">

**Author:** ![Jeehut](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jeehut/32/21743_2.png) [@Jeehut](https://forums.swift.org/u/Jeehut)\
**Post date:** [May 29, 2024, 1:56pm UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/36 "2024-05-29T13:56:43Z")

</div>

After discovering this thread, I decided I would add a similar extension to my projects. But to me the most consistent naming would be this:

```swift
extension Optional {
   func get(orThrow error: @autoclosure () -> some Error) throws -> Wrapped {
      guard let unwrapped = self else { throw error() }
      return unwrapped
   }
}

```

Why? Like I've explained in the collapsible disclosure section [here](https://forums.swift.org/t/if-guard-let-catch-for-conveniently-accessing-both-result-type-cases/58492), an `Optional` is nothing else but a `Result` where the failure case `.none` has no additional information about the _reason_ it is `.none`. In other words, an `Optional<T>` type is syntactic sugar for `Result<T, VoidError>`.

And what API does the `Result` type already contain that unwraps the success case or throws an error? Yes, it's the [get function](https://developer.apple.com/documentation/swift/result/get()). The only difference to that function is that in the case of `Optional` the error needs to be passed as a parameter as it's not included int he `Optional` type, so we have to pass `orThrow:`.

The result is an API call site that reads like this:

```swift
someOptional.get(orThrow: MyError.unexpectedlyFoundNil)

```

---

<div class="post-metadata">

**Author:** ![Jon\_Shier](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_shier/32/921_2.png) [@Jon\_Shier](https://forums.swift.org/u/Jon_Shier)\
**Post date:** [May 29, 2024, 2:11pm UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/37 "2024-05-29T14:11:10Z")

</div>

`get()` is an aberration added in a single round of the `Result` review with no external discussion (it was the second or third review at that point), so it shouldn't be used as justification for further naming. We can see that reflected in `Task`, which was going to originally use `get()` to access its produced value, but went with `value` after async property accessors were added (specifically to get that superior naming).

If `Result.get()` were named today it would likely be similar:

```swift
var value: Success {
  get throws(Failure) {
    // .success
  }
}

```

Even if none of that were true, `get()` just isn't a good name, and there doesn't seem to be a compelling reason to align `Optional` and `Result` here in the first place. Personally I think `unwrapped(orThrow:)`, with a default error provided, is the clearest and most ergonomic version. I'd be happy with a version that can't throw a custom error too.

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [May 29, 2024, 6:11pm UTC](https://forums.swift.org/t/pitch-optional-orthrow/69995/38 "2024-05-29T18:11:42Z")

</div>

I summarised options currently available via normal language means:

 ![image](https://global.discourse-cdn.com/swift/original/3X/e/a/eaf43a03afcdd6e8e511fb067296070a86e1bbfb.jpeg)

Neither option allows ticking all the boxes.

[Previous page](https://forums.swift.org/t/pitch-optional-orthrow/69995.md?page=1)
