Swift 5.9参数包:如何将元组展开为函数参数?
Swift 5.9中参数包元组展开的可行解决方案
问题背景
以下依赖注入包的代码在Swift 5.9中无法编译:
func register<T, each A>( _ type: T.Type = T.self, tags: AnyHashable..., lifetime: Lifetime = .transient, factory: @escaping (any Resolver, repeat each A) throws -> T ) -> Key { let thunk = { (r: any Resolver, args: (repeat each A)) throws -> T in try factory(r, repeat each args) } return register(type, tags: Set(tags), lifetime: lifetime, factory: thunk) }
编译时触发INTERNAL ERROR: feature not implemented: reabstraction of pack values。核心原因是:闭包thunk中尝试将参数包生成的元组args展开后传入factory,但该特性在Swift 5.9中暂未实现。
可行解决方案
方案1:手动重载到固定参数数量(最稳定)
由于Swift 5.9不支持原生的元组参数包展开,最稳妥的方式是为不同参数数量的场景手动实现重载函数,覆盖你需要支持的参数个数上限:
// 无参数工厂函数版本 func register<T>( _ type: T.Type = T.self, tags: AnyHashable..., lifetime: Lifetime = .transient, factory: @escaping (any Resolver) throws -> T ) -> Key { let thunk = { (r: any Resolver, args: ()) throws -> T in try factory(r) } return register(type, tags: Set(tags), lifetime: lifetime, factory: thunk) } // 单参数工厂函数版本 func register<T, A>( _ type: T.Type = T.self, tags: AnyHashable..., lifetime: Lifetime = .transient, factory: @escaping (any Resolver, A) throws -> T ) -> Key { let thunk = { (r: any Resolver, args: (A)) throws -> T in try factory(r, args.0) } return register(type, tags: Set(tags), lifetime: lifetime, factory: thunk) } // 双参数工厂函数版本 func register<T, A, B>( _ type: T.Type = T.self, tags: AnyHashable..., lifetime: Lifetime = .transient, factory: @escaping (any Resolver, A, B) throws -> T ) -> Key { let thunk = { (r: any Resolver, args: (A, B)) throws -> T in try factory(r, args.0, args.1) } return register(type, tags: Set(tags), lifetime: lifetime, factory: thunk) } // 按需扩展到更多参数(比如3~6个参数)
这种方式虽然需要编写重复代码,但兼容性最好,不会依赖未实现的Swift特性。
方案2:利用辅助协议间接展开元组
通过定义一个辅助协议,让不同长度的元组遵循该协议并实现展开逻辑,从而间接调用原工厂函数:
// 辅助协议,定义元组展开逻辑 protocol TupleUnpacker { associatedtype Output func callFactory(with resolver: any Resolver, factory: (any Resolver, Self) throws -> Output) throws -> Output } // 空元组扩展 extension Void: TupleUnpacker { typealias Output = Any func callFactory(with resolver: any Resolver, factory: (any Resolver, Void) throws -> Output) throws -> Output { try factory(resolver, ()) } } // 单元素元组扩展 extension TupleUnpacker where Self == (A), A: Sendable { typealias Output = Any func callFactory(with resolver: any Resolver, factory: (any Resolver, A) throws -> Output) throws -> Output { try factory(resolver, self.0) } } // 双元素元组扩展 extension TupleUnpacker where Self == (A, B), A: Sendable, B: Sendable { typealias Output = Any func callFactory(with resolver: any Resolver, factory: (any Resolver, A, B) throws -> Output) throws -> Output { try factory(resolver, self.0, self.1) } } // 按需扩展更多长度的元组
修改原register函数,利用该协议实现调用:
func register<T, each A>( _ type: T.Type = T.self, tags: AnyHashable..., lifetime: Lifetime = .transient, factory: @escaping (any Resolver, repeat each A) throws -> T ) -> Key where (repeat each A): TupleUnpacker, (repeat each A).Output == T { let thunk = { (r: any Resolver, args: (repeat each A)) throws -> T in try args.callFactory(with: r) { resolver, unpacked in try factory(resolver, unpacked) } } return register(type, tags: Set(tags), lifetime: lifetime, factory: thunk) }
这种方式更贴近参数包的原生用法,但同样需要为每个元组长度编写扩展,且对泛型约束的要求更严格。
总结
Swift 5.9中没有原生支持参数包元组的直接展开,上述两种方案是当前可行的替代方案:
- 若追求稳定性和兼容性,优先选择手动重载方案;
- 若希望尽可能贴近参数包的设计思路,可以尝试辅助协议方案。
内容的提问来源于stack exchange,提问作者Gregory Higley
相关产品推荐
相关产品推荐

