Skip to content

SE-0465: Do not generalize Result.flatMapError yet #2742

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 18, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 4 additions & 10 deletions proposals/0465-nonescapable-stdlib-primitives.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ We generalize `Result` along the same lines as `Optional`, allowing its `success

Important convenience APIs such as `Result.init(catching:)` or `Result.map` will need to require escapability until we introduce a way to formally specify lifetime dependencies. This is unfortunate, but it still enables intrepid Swift developers to experiment with defining interfaces that take (or perhaps even return!) `Result` values.

However, we are already able to generalize a small handful of methods: `get` and the two error-mapping utilities, `mapError` and `flatMapError`.
However, we are already able to generalize a couple of methods: `get` and the error-mapping utility `mapError`.

```swift
func sample<E: Error>(_ res: Result<Span<Int>, E>) -> Int {
Expand Down Expand Up @@ -525,7 +525,7 @@ extension Result: Escapable where Success: Escapable & ~Copyable {}
extension Result: Sendable where Success: Sendable & ~Copyable & ~Escapable {}
```

We postpone generalizing most of the higher-order functions that make `Result` convenient to use, as we currently lack the means to reason about lifetime dependencies for such functions. But we are already able to generalize the two functions that do not have complicated lifetime semantics: `mapError` and `flatMapError`.
We postpone generalizing most of the higher-order functions that make `Result` convenient to use, as we currently lack the means to reason about lifetime dependencies for such functions. But we are already able to generalize the one function that does not have complicated lifetime semantics: `mapError`.

```swift
extension Result where Success: ~Copyable & ~Escapable {
Expand All @@ -534,16 +534,9 @@ extension Result where Success: ~Copyable & ~Escapable {
_ transform: (Failure) -> NewFailure
) -> Result<Success, NewFailure>
}

extension Result where Success: ~Copyable & ~Escapable {
@_lifetime(copying self) // Illustrative syntax
consuming func flatMapError<NewFailure>(
_ transform: (Failure) -> Result<Success, NewFailure>
) -> Result<Success, NewFailure>
}
```

Both of these functions return a value with the same lifetime as the original `Result` instance.
The returned value has the same lifetime constraints as the original `Result` instance.

We can also generalize the convenient `get()` function, which is roughly equivalent to optional unwrapping:

Expand Down Expand Up @@ -888,5 +881,6 @@ Many people contributed to the discussions that led to this proposal. We'd like
- Guillaume Lessard
- John McCall
- Tony Parker
- Ben Rimmington
- Andrew Trick
- Rauhul Varma