Disable "Constant inferred to have type '()'" warning when using async let

Swift warns when you assign a Void result to a variable:

func updateState() {
  // Update internal state here. Note the lack of a return type
}

// ⚠️: Constant 'x' inferred to have type '()', which may be unexpected
let x = updateState()

This is usually a useful warning; usually if you’re assigning to a variable, you want to be able to do something with it, and there’s not much you can do with a Void.

However, if we’re using async let, this is less useful:

// Run both tasks in parallel
async let task1 = updateState()     // ⚠️: Constant 'task1' inferred...
async let task2 = updateMoreState() // ⚠️: Constant 'task2' inferred...

await (task1, task2)

This warning is less useful now, because now there is something useful we can do with the void return value — we can await it.

You can silence this warning by explicitly declaring the type:

// Run both tasks in parallel
async let task1: () = updateState()     // ✅ No more warning
async let task2: () = updateMoreState() // ✅ No more warning

await (task1, task2)

But it’s annoying to have to do that.

Any thoughts on removing this warning? Is this the kind of thing we’d need a formal proposal for? Or could I just submit a PR?

12 Likes

I think removing the warning here is a good idea. In the async let case you aren’t really returning void any more, you are sort of returning a promise of void, which isn’t the same thing at all, because there is a use for the assigned variable.

This is a change to a compiler warning and doesn't require evolution approval.

I do think it's a good change to make.

5 Likes

Additionally, you did not note the following warning, which requires an assignment like the following, to be silenced.

_ = await (task1, task2)
async let task1: Void = updateState()
async let task2: Void = updateMoreState()
await (task1, task2) // Expression of type '(Void, Void)' is unused

Ideally, I'd like to see that gone too. But as it is, I don't think the compiler should allow awaiting on multiple async lets in a tuple, because it's misleading and broken when matched up with the results of multiple calls stuffed into a tuple.

func wait() async {
  try! await Task.sleep(for: .seconds(1))
  print(#function)
}
func doNotWait()  { print(#function) }

async let (wait, doNotWait): (_, _) = (wait(), doNotWait())
_ = await (doNotWait, wait) // "wait()", "doNotWait()" 👎
async let (doNotWait, wait): (_, _) = (doNotWait(), wait())
_ = await (wait, doNotWait) //  "doNotWait()", "wait()" 👍

Neat idea, very supportive of cleaning that up, please give it a shot or reach out if you’d have trouble figuring out how to make the change :-)