Swift, где расширения массива

Что касается 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. Кому-нибудь здесь повезло?


person yo.ian.g    schedule 10.06.2015    source источник


Ответы (1)


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

определяет общий метод filterWithId(), где общий заполнитель T ограничен значением Idable. Но это определение вводит локальный заполнитель 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
        }
    }
}
person Martin R    schedule 10.06.2015
comment
Есть ли способ сделать это с непротокольными типами, такими как extension Array where Iterator.Element : CGRect? - person Ky Leggiero; 07.11.2016
comment
@BenLeggiero: вы можете определить extension Sequence where Iterator.Element == CGRect { }, то есть для протокола, но тип Element Array может быть ограничен только протоколом. Это может измениться в будущем. - person Martin R; 07.11.2016