Swift 5.5 async可选值使用??运算符调用async let默认值报错如何处理
结论
你遇到的报错不是语法错误,是Swift标准库目前没有提供支持异步参数的??运算符重载,原生??运算符的右值默认不支持异步求值。
原因说明
- 原生
??运算符的右参数被标记为@autoclosure,会自动把传入的表达式包裹成无参数闭包,实现“仅当前面的可选值为nil时才执行后面表达式”的短路逻辑。 - 标准库当前的
??重载没有给这个自动闭包添加async支持,所以闭包内部不允许执行需要await的操作;而async let定义的变量取值时需要隐式执行await,自然就触发了编译器报错:
'async let' in an autoclosure that does not support concurrency
- 你写的
try await langRes ?? ""可以正常运行,是因为""是同步值,不需要await求值,不会触发并发限制。
解决方法
你可以自定义支持异步参数的??运算符重载,代码如下:
infix operator ?? : NilCoalescingPrecedence // 右值为可选类型的异步空合并重载 public func ?? <T>(optional: T?, defaultValue: @autoclosure () async throws -> T?) async rethrows -> T? { guard let optional = optional else { return try await defaultValue() } return optional } // 右值为非可选类型的异步空合并重载 public func ?? <T>(optional: T?, defaultValue: @autoclosure () async throws -> T) async rethrows -> T { guard let optional = optional else { return try await defaultValue() } return optional }
添加重载后即可用类似原生??的写法实现需求,注意异步值前需要加await:
// 方案1:两个查询并发执行,无论第一个是否有值,第二个都会提前启动 async let langRes = try traverse(file: languageFile, for: string) async let engRes = try traverse(file: englishFile, for: string) let result = try await langRes ?? await engRes // 方案2:仅第一个查询结果为nil时才执行第二个查询,节省IO资源 let langRes = try await traverse(file: languageFile, for: string) let result = try await langRes ?? traverse(file: englishFile, for: string)
内容的提问来源于stack exchange,提问作者Darren
相关产品推荐
相关产品推荐

