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

Swift "where" Расширения массива

С Swift 2.0 кажется, что мы можем приблизиться к расширениям типовых типов, применимых к предполагаемым ситуациям.

Хотя мы все еще не можем этого сделать:

protocol Idable {
    var id : String { get }
}

extension Array where T : Idable {
    ...
}

... теперь мы можем сделать это:

extension Array {
    func filterWithId<T where T : Idable>(id : String) -> [T] {
    ...
    }
}

... и Swift грамматически принимает это. Однако для жизни я не могу понять, как сделать компилятор счастливым, когда я заполняю содержимое функции примера. Предположим, что я должен быть максимально явным:

extension Array {
    func filterWithId<T where T : Idable>(id : String) -> [T] {
        return self.filter { (item : T) -> Bool in
            return item.id == id
        }
    }
}

... компилятор не примет закрытие, предоставленное для фильтрации, жалуясь

Невозможно вызвать 'фильтр' с помощью списка аргументов типа '((T) → Bool)'

Аналогично, если элемент указан как Idable. Кому-нибудь повезло?

4b9b3361

Ответ 1

extension Array {
    func filterWithId<T where T : Idable>(id : String) -> [T] {
    ...
    }
}

определяет общий метод filterWithId(), где общий placeholder T ограничивается Idable. Но это определение вводит локальный placeholder T который полностью не связан с типом элемента массива T (и скрывает это в рамках метода).

Итак, вы не указали, что элементы массива должны соответствовать до Idable, и именно по этой причине вы не можете позвонить  self.filter() { ... } с замыканием, которое ожидает, что элементы Idable.

С Swift 2/Xcode 7 beta 2 вы можете определить методы расширения для общего типа, которые более ограничивают шаблон (сравните Расширение массива, чтобы удалить объект по значению для очень похожей проблемы):

extension Array where Element : Idable {

    func filterWithId(id : String) -> [Element] {
        return self.filter { (item) -> Bool in
            return item.id == id
        }
    }
}

В качестве альтернативы вы можете определить способ расширения протокола:

extension SequenceType where Generator.Element : Idable {

    func filterWithId(id : String) -> [Generator.Element] {
        return self.filter { (item) -> Bool in
            return item.id == id
        }
    }
}

Затем filterWithId() доступен для всех типов, соответствующих к SequenceType (в частности, к Array), если элемент последовательности тип соответствует Idable.

В Swift 3 это будет

extension Sequence where Iterator.Element : Idable {

    func filterWithId(id : String) -> [Iterator.Element] {
        return self.filter { (item) -> Bool in
            return item.id == id
        }
    }
}