不使用关联类型,如何实现RawRepresentable(String)的协议变量及函数参数约束?
Swift 协议约束相关问题解答
问题1:不使用associated type的情况下,能否将协议中的变量限制为仅支持Raw type为String的RawRepresentable类型?
当然可以!在Swift 5.7及以上版本,我们可以利用存在类型(any)和where子句直接在协议中对变量类型进行约束,完全不需要associated type。
举个具体的实现例子:
protocol StringRawRepresentableHolder { // 要求变量必须是 RawValue 为 String 的 RawRepresentable 类型 var restrictedVar: any RawRepresentable where RawValue == String { get } }
这样一来,任何实现这个协议的类型,都必须给restrictedVar赋值一个满足条件的类型(比如RawValue为String的枚举,甚至String本身,因为String也符合RawRepresentable)。如果尝试给它赋值RawValue为Int的枚举或者其他不符合的类型,编译器会直接报错。
正确的实现示例:
struct ValidHolder: StringRawRepresentableHolder { enum MyEnum: String { case foo case bar } var restrictedVar: some RawRepresentable where RawValue == String { MyEnum.foo } }
错误的实现会直接编译失败:
struct InvalidHolder: StringRawRepresentableHolder { enum MyEnum: Int { // RawValue 是 Int,不符合约束 case one case two } var restrictedVar: some RawRepresentable where RawValue == Int { MyEnum.one } // 类型不匹配,报错 }
问题2:定义函数接收一组对象,要求每个对象必须有基于String类型的名为“Commands”的枚举,如何实现并阻止非String RawType的代码编译?
你提到用associated type遇到了问题——确实,带associated type的协议默认不能直接作为集合的元素类型(因为它是泛型协议),但我们可以结合associated type约束和**存在类型(any)**来解决这个问题,同时严格限制用户的实现。
解决方案步骤:
- 定义一个协议,用associated type强制要求类型必须包含名为
Commands的枚举,且这个枚举的RawValue必须是String:
protocol HasStringBasedCommands { // 直接在associated type上添加约束,确保Commands是RawValue为String的RawRepresentable associatedtype Commands: RawRepresentable where Commands.RawValue == String }
- 定义函数时,使用
any HasStringBasedCommands作为集合元素的存在类型:
func processCommandObjects(_ objects: [any HasStringBasedCommands]) { // 在这里编写你的处理逻辑,比如遍历对象并访问它们的Commands枚举 for object in objects { // 如果需要访问Commands的具体值,可以在协议中添加对应方法,比如: // func defaultCommand() -> Commands } }
效果验证:
- 符合要求的类型可以正常实现协议并被函数接收:
struct UserAction: HasStringBasedCommands { enum Commands: String { case login case logout } } struct DeviceControl: HasStringBasedCommands { enum Commands: String { case powerOn case powerOff } } // 可以混合不同类型的对象传入函数 let objects: [any HasStringBasedCommands] = [UserAction(), DeviceControl()] processCommandObjects(objects)
- 如果用户尝试定义RawValue非String的
Commands枚举,编译会直接失败:
struct InvalidObject: HasStringBasedCommands { enum Commands: Int { // RawValue是Int,不满足协议约束,编译报错 case increment case decrement } }
这种方式既满足了对Commands枚举的类型约束,又解决了associated type不能作为集合类型的问题,完全符合你的需求——不符合要求的代码会在编译阶段就被拦截。
内容的提问来源于stack exchange,提问作者Mark A. Donohoe
相关产品推荐
相关产品推荐

