It cannot. Or at least, I'm not aware of any way that it could be, using currently existing language features.
First and foremost, due to the lack of async defer and async deinit, which is being discussed in a separate thread. You'd basically have to manually add the cancel and await logic to every possible exit point of the scope, including every single try. And you'd need to constantly maintain it every time a new try or return is added to the scope.
Second, because, as mentioned in a different thread yet again, while you can transfer cancellation signals to a manually created task, the same is not so simple with regards to task-local values.
And mind you that, despite all of this, I actually think async let was mistake. If we had async deinit, and some way of transferring task locals, then async let could have been implemented as a class/struct instead, with such extra features as being able to return it to a caller, or have it as a member variable of another class, or even adding it to an array allowing it to cover TaskGroup as well. And my opinion is that if it could have, then it should have.
Actually, the default behavior for an async let that is not awaited is to first cancel it, and only then await. Which is probably the farthest thing from what you want to happen, as it means that as soon as your side-effect function waits on anything cancellable, it will actually stop running.