Можно ли добавить ограничения типа в расширение соответствия протокола Swift?
Я хотел бы расширить Array
чтобы добавить соответствие новому протоколу - но только для массивов, чьи элементы сами соответствуют определенному протоколу.
В более общем смысле, я хотел бы, чтобы типы (будь то протоколы или конкретные типы) с параметрами типа реализовывали протокол только тогда, когда параметры типа соответствуют определенным ограничениям.
Начиная с Swift 2.0 это кажется невозможным. Есть ли способ, которым я скучаю?
пример
Предположим, у нас есть Friendly
протокол:
protocol Friendly {
func sayHi()
}
Мы можем расширить существующие типы для его реализации:
extension String: Friendly {
func sayHi() {
print("Greetings from \(self)!")
}
}
"Sally".sayHi()
Мы также можем расширить Array
для реализации sayHi()
когда все его элементы являются Friendly
:
extension Array where Element: Friendly {
func sayHi() {
for elem in self {
elem.sayHi()
}
}
}
["Sally", "Fred"].sayHi()
На этом этапе тип [Friendly]
должен сам реализовать Friendly
, поскольку он соответствует требованиям протоколов. Тем не менее, этот код не компилируется:
extension Array: Friendly where Element: Friendly {
func sayHi() {
for elem in self {
elem.sayHi()
}
}
}
Сообщение об ошибке: "расширение типа" Массив "с ограничениями не может иметь условия наследования", что, похоже, окончательно закрывает дверь при таком прямом подходе.
Есть ли косвенный обходной путь? Какой-нибудь умный трюк, который я могу использовать? Возможно, есть способ расширения SequenceType
вместо Array
?
Рабочее решение позволит скомпилировать этот код:
let friendly: Friendly = ["Foo", "Bar"]
Обновление: это появилось в Swift 4.1, и это красота!
extension Array: Friendly where Element: Friendly
пример extension Array: Friendly where Element: Friendly
теперь компилируется, как указано в исходном вопросе.
Ответы
Ответ 1
РЕДАКТИРОВАТЬ: Как отмечено в обновленном вопросе, это стало возможным начиная с Swift 4.1
В настоящее время это невозможно в Swift (начиная с Xcode 7.1). Как указывает ошибка, вы не можете ограничить соответствие протокола ("условие наследования") расширением с ограничением по типу. Возможно когда-нибудь. Я не верю, что есть какая-то серьезная причина, по которой это невозможно, но в настоящее время оно не реализовано.
Самое близкое, что вы можете получить, это создать тип оболочки, такой как:
struct FriendlyArray<Element: Friendly>: Friendly {
let array: [Element]
init(_ array: [Element]) {
self.array = array
}
func sayHi() {
for elem in array {
elem.sayHi()
}
}
}
let friendly: Friendly = FriendlyArray(["Foo", "Bar"])
(Вы, вероятно, захотите расширить FriendlyArray
до класса CollectionType
.)
Для рассказа о моем собственном происхождении в безумии попытки сделать эту работу и моем ползании с края, см. NSData, Мой Старый Друг.
Ответ 2
Хорошая новость заключается в том, что то, что вы просите об Conditional Conformance
в Swift 4.1:
https://swift.org/blog/conditional-conformance/