0

According to Preserving the Results of a Throwing Expression documentation it is possible to convert throwing expression into Result type as follows:

let singleSample = Result { try UnreliableRandomGenerator().random() }

Is it also possible to convert async throwing expression into Result type like:

let singleSample = Result { try await UnreliableRandomGenerator().random() } 
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
softshipper
  • 32,463
  • 51
  • 192
  • 400
  • Find an example to test with and see what happens. – Solomon Ucko May 19 '23 at 12:18
  • @matt How are they opposites? Aren't they instead orthogonal/independent? – Solomon Ucko May 19 '23 at 12:20
  • 1
    You could do it with a do try catch, I use it when I have some inner function that can “throw” but I don’t consider an inner throw worthy of terminating the whole operation such as decoding an array, partial values are better than no values and the error doesn’t get ignored. – lorem ipsum May 19 '23 at 12:27
  • [here](https://stackoverflow.com/questions/75236895/how-do-i-use-weatherkit-swift-framework-to-fetch-the-weather-inside-a-widget-ext/75237499#75237499) is a sample, widgets is another good place since there is no built in way to present errors. – lorem ipsum May 19 '23 at 12:39
  • 1
    @matt "The point of the async/await revolution is that you don't need Result any more." `Result` doesn't really have anything to do with sync/async code, it can be used for both. I actually think `Result` still has value, since it has strongly typed errors, which unfortunately the language doesn't support with `throws`. If only the call-sites wouldn't become cumbersome when using `Result` instead of throwing methods... – Dávid Pásztor May 19 '23 at 12:58
  • For now, I just got `let singleSample2 = await Result.make { try await UnreliableRandomGenerator().asyncRandom() }`, where I have made `extension Result where Failure == Error { static func make(catching body: () async throws -> Success) async -> Result { do { let output = try await body(); return .success(output) } catch { return .failure(error) } } }`, I guess there are room for improvement if the behavior doesn't exist already. But I guess the issue is the `init` with an await inside. – Larme May 19 '23 at 13:40

1 Answers1

6

There is no built-in way. You cannot pass an async closure to the init in stdlib today. It is pretty trivial to add (based on the stdlib code):

extension Result where Failure == Swift.Error {
  public init(catching body: () async throws -> Success) async {
    do {
      self = .success(try await body())
    } catch {
      self = .failure(error)
    }
  }
}

But note that this makes the init async, so your code would need to be:

let singleSample = await Result { try await UnreliableRandomGenerator().random() }
                   ^^^^^

This would of course have to be called in an async context.

It's possible that what you're trying to do is make a "Future" so you can pass it through synchronous contexts. That already exists. It's called Task:

let singleSample = Task { try await UnreliableRandomGenerator().random() }

When you want to resolve this into a Result, you can await its .result property:

let result = await singleSample.result

Or you can skip the Result, and access its value directly with try-value:

let result = try await singleSample.value

In many cases, Result isn't all that useful in the Structured Concurrency world. It's better to use try and await directly. But it can still be very helpful for storing in properties, passing through channels that allow errors, and interacting with non-structured code, so the .result property on Task is very welcome, and it's nice that it easily can convert to and from throws.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610