Протокол Swift с associatedtype (неоднозначный для поиска типа)
Мне нужно создать универсальную функцию в протоколе с реализацией по умолчанию в расширении. It func должен работать с элементом как enum:RawRepresentable где RawValue == String всегда. Я пытался
protocol RequiresEnum: class {
associatedtype SectionIdentifierEnum: RawRepresentable // how this add restriction to RawValue == String
func test<T: SectionIdentifierEnum>(identifier: T) where T.RawValue == String
}
enum RequiresEnumDefault: String {
case `default`
}
extension RequiresEnum where Self: UIViewController, SectionIdentifierEnum.RawValue == String {
typealias SectionIdentifierEnum = RequiresEnumDefault
func test<T: SectionIdentifierEnum>(identifier: T) where T.RawValue == String {
print(T.rawValue)
}
}
Но у меня есть ошибки
- 'SectionIdentifierEnum' является неоднозначным для поиска типа в этом контексте
- 'RawValue' не является членом типа 'T'
Любые решения
1 ответ:
В общем случае, когда речь идет о генериках в контексте протоколов, универсальный тип-заполнитель рассматривается как репрезентируемый
associatedtypeпротокола. В вашем примере это будетSectionIdentifierEnum, который действует как заполнитель для ограниченного типа .SectionIdenfierEnum, однако, не является протоколом сам по себе, поэтому не может использовать его в качестве ограничения типа в универсальном методе. Вы можете, однако, использовать его в качестве самого типа в вашем методеtest(...).
Swift 3.1
Сейчас, в настоящее время (Swift 3.1), вы не можете добавить сложные ограничения типа к
associatedtype. Однако вы можете предоставить реализацию по умолчанию, доступную только для случая, когда whereSelfпроисходит отUIViewControllerи реализует протоколRequiresEnum, задав ТипSectionIdentifierEnumконкретному типуRequiresEnumDefault. Последний установит, что ассоциированныйRawValueявляетсяStringдля этой реализации по умолчанию, посколькуRawValueконкретного типаRequiresEnumDefaultявляетсяString.Например:
// Swift 3.1 // --------- // Types that implement this protocol mustn't necessarily use a // `SectionIdentifierEnum` type where `SectionIdentifierEnum.RawValue` // is constrained to equal `String`. protocol RequiresEnum: class { associatedtype SectionIdentifierEnum: RawRepresentable func test(identifier: SectionIdentifierEnum) } enum RequiresEnumDefault: String { case `default` } // This extension, however, is only available for types that use // `RequiresEnumDefault ` as the concrete type of `SectionIdentifierEnum` // (in which case `SectionIdentifierEnum.RawValue` is `String`). extension RequiresEnum where Self: UIViewController, SectionIdentifierEnum == RequiresEnumDefault { func test(identifier: SectionIdentifierEnum) { print(identifier.rawValue) } } // Example usage. class MyViewController : UIViewController, RequiresEnum { typealias SectionIdentifierEnum = RequiresEnumDefault // ... } let foo = MyViewController() foo.test(identifier: RequiresEnumDefault.default) // prints "default" (using extension:s default implementation)Выше, реализация по умолчанию
test(...)доступен только тогда, когдаSectionIdentifierEnumравен конкретному типуRequireEnumDefault(иSelfпроисходит отUIViewController...). Если вместо этого вы хотите, чтобы он был доступен только тогда, когдаSectionIdentifierEnumявляется любым перечислением с типомStringRawValue, вы изменяете ограничение типа расширений соответственно:protocol RequiresEnum: class { associatedtype SectionIdentifierEnum: RawRepresentable func test(identifier: SectionIdentifierEnum) } enum RequiresEnumDefault: String { case `default` } extension RequiresEnum where Self: UIViewController, SectionIdentifierEnum.RawValue == String { func test(identifier: SectionIdentifierEnum) { print(identifier.rawValue) } } // Example usage. enum EnumWithStringRawValue: String { case foo } class MyViewController : UIViewController, RequiresEnum { typealias SectionIdentifierEnum = EnumWithStringRawValue // ... } let foo = MyViewController() foo.test(identifier: EnumWithStringRawValue.foo) // prints "foo" (using extension:s default implementation)
Как только Swift 4 будет выпущен, вы сможете добавить более сложные ограничения к
associatedtype: s, согласно реализации предложения Swift evolution:В этом случае вышеизложенное может быть изменено на:
// Swift 4 // ------- // Here, all types that implement this protocol must use a // `SectionIdentifierEnum` type where `SectionIdentifierEnum.RawValue` // is equal to `String`. protocol RequiresEnum: class { associatedtype SectionIdentifierEnum: RawRepresentable where SectionIdentifierEnum.RawValue == String func test(identifier: SectionIdentifierEnum) } enum RequiresEnumDefault: String { case `default` } // For the specific case where `SectionIdentifierEnum` equals // `RequiresEnumDefault` (and where `Self` derives from `UIViewController`), // this default implementation is readily available. extension RequiresEnum where Self: UIViewController, SectionIdentifierEnum == RequiresEnumDefault { func test(identifier: SectionIdentifierEnum) { print(identifier.rawValue) } } // Example usage. class MyViewController : UIViewController, RequiresEnum { typealias SectionIdentifierEnum = RequiresEnumDefault // ... } let foo = MyViewController() foo.test(identifier: RequiresEnumDefault.default) // prints "default" (using extension:s default implementation)Аналогично модифицируя ограничение на реализацию по умолчанию
test(...)не только для случая, когдаSectionIdentifierEnumравноRequiresEnumDefault(но и для любого перечисления: в этом случае мы знаем, что такое перечисление всегда будет иметьRawValueString, из-за ограничения наassociatedtypeв определении протокола).protocol RequiresEnum: class { associatedtype SectionIdentifierEnum: RawRepresentable where SectionIdentifierEnum.RawValue == String func test(identifier: SectionIdentifierEnum) } enum RequiresEnumDefault: String { case `default` } // From the constraint on the `RawValue` of `SectionIdentifierEnum` // above, we know that `RawValue` equals `String`. extension RequiresEnum where Self: UIViewController { func test(identifier: SectionIdentifierEnum) { print(identifier.rawValue) } } // Example usage. enum EnumWithStringRawValue: String { case foo } class MyViewController : UIViewController, RequiresEnum { typealias SectionIdentifierEnum = EnumWithStringRawValue // ... } let foo = MyViewController() foo.test(identifier: EnumWithStringRawValue.foo) // prints "foo" (using extension:s default implementation)