Where to put try using async let

We had a discussion at work about where to put the try keyword when using async let.

Let’s say we have a function defined like this:

func bar() async throws -> Response?

A colleague wanted to use async let to start the call, then await the response later in their task. They also wanted to utilise the try? operator to just let the expected return value become nil if the call throws an error. They wrote their task like this:

Task {
    async let foo = try? bar()
    // ... other stuff
    self.response = await foo
}

This didn’t sit right with me, because in my experience you would always put the try with the await. I am wondering why Xcode didn’t throw an error here, so I experimented a bit and found that if we use a try without the ? in the same way, we DO get an error:

Task {
    async let foo = try bar()
    // ... other stuff
    self.response = await foo    // This line errors, see below
}

Screenshot 2026-01-06 at 17.18.09

That is the error I expected for the above case, and to me that signifies that the try on the async let declaration does nothing. However, that still leaves me wondering why my colleague didn’t get a compiler error with their code.

Is this a compiler issue? Or does the try? operator lead to the correct result when an error is thrown? According to my colleague, their code also delivered the expected results, but I don’t know how extensively it was tested.

try? turns a throwing subexpression into a non-throwing one by discarding the error. (You can see foo has a different type in the two examples.) So it’s “correct” that both versions compile, but they don’t do the same thing…except that as written, you don’t check on the result of the task, so you’re discarding the error either way.

4 Likes