Подтвердить что ты не робот

Можно ли добавить ограничения типа в расширение соответствия протокола 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 теперь компилируется, как указано в исходном вопросе.

4b9b3361

Ответ 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, Мой Старый Друг.