Swift中如何在编译期强制数组元素为非可选类型?
问题描述
给定代码示例:
struct MyStruct { @ArrayOfNonOptionalElements var arr: [SomeNonOptionalType] }
其中@ArrayOfNonOptionalElements是一个propertyWrapper。请问如何实现该属性包装器,以强制数组元素类型为非可选?是否可行?
更新1:似乎没人理解我的问题,我希望编译器阻止在方括号内添加问号(即禁止元素为可选类型)。
更新2:补充说明:我正在编写用于安全数组解码的"@Compact"属性包装器,确保结果不含任何nil值,它可适配任意数组类型,这算是一个完美主义者的小挑战。
实现方案
完全可以实现,核心通过泛型约束在编译阶段限制数组元素为非可选类型,同时完成安全解码的逻辑:
1. 核心思路
利用Swift泛型的特性,给属性包装器添加约束,让它只接受元素为非可选的数组;针对可选元素的数组,通过标记不可用的初始化方法触发编译报错。解码时先处理含nil的数据源,过滤后得到纯非可选元素的数组。
2. 具体代码实现
@propertyWrapper struct Compact<Element: Decodable> { var wrappedValue: [Element] init(wrappedValue: [Element]) { self.wrappedValue = wrappedValue } } // 扩展实现安全解码逻辑:自动过滤nil extension Compact: Decodable { init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() // 先解码成带可选元素的数组,再过滤nil let optionalArray = try container.decode([Element?].self) self.wrappedValue = optionalArray.compactMap { $0 } } } // 关键:禁止元素为可选类型的数组 extension Compact where Element: ExpressibleByNilLiteral { @available(*, unavailable, message: "Compact不允许数组元素为可选类型,请移除方括号内的问号") init(wrappedValue: [Element]) { fatalError("不可用") } }
3. 使用效果
// 正确用法:元素为非可选类型,编译通过 struct ValidStruct: Decodable { @Compact var arr: [String] } // 错误用法:尝试使用可选元素数组,编译器直接报错 struct InvalidStruct: Decodable { @Compact var arr: [String?] // 编译提示:'init(wrappedValue:)' is unavailable: Compact不允许数组元素为可选类型,请移除方括号内的问号 }
4. 原理说明
- 泛型
Element: Decodable确保元素可解码,同时利用ExpressibleByNilLiteral协议(所有可选类型都遵循该协议),给可选元素的数组场景提供一个标记为unavailable的初始化方法,触发编译报错,精准阻止用户在方括号内加问号。 - 解码逻辑中,先将原始数据解码为包含可选元素的数组,再通过
compactMap过滤掉所有nil值,最终得到纯非可选元素的数组,满足安全解码的需求。
内容的提问来源于stack exchange,提问作者Roman
相关产品推荐
相关产品推荐

