As of Swift 2.0 it seems we can get closer to extensions of generic types applicable to predicated situations.
Although we still can't do this:
protocol Idable { var id : String { get } } extension Array where T : Idable { ... }
...we can now do this:
extension Array { func filterWithId<T where T : Idable>(id : String) -> [T] { ... } }
...and Swift grammatically accepts it. However, for the life of me I cannot figure out how to make the compiler happy when I fill in the contents of the example function. Suppose I were to be as explicit as possible:
extension Array { func filterWithId<T where T : Idable>(id : String) -> [T] { return self.filter { (item : T) -> Bool in return item.id == id } } }
...the compiler will not accept the closure provided to filter, complaining
Cannot invoke 'filter' with an argument list of type '((T) -> Bool)'
Similar if item is specified as Idable. Anyone had any luck here?
extension Array { func filterWithId<T where T : Idable>(id : String) -> [T] { ... } }
defines a generic method filterWithId()
where the generic placeholder T
is restricted to be Idable
. But that definition introduces a local placeholder T
which is completely unrelated to the array element type T
(and hides that in the scope of the method).
So you have not specified that the array elements must conform to Idable
, and that is the reason why you cannot call self.filter() { ... }
with a closure which expects the elements to be Idable
.
As of Swift 2 / Xcode 7 beta 2, you can define extension methods on a generic type which are more restrictive on the template (compare Array extension to remove object by value for a very similar issue):
extension Array where Element : Idable { func filterWithId(id : String) -> [Element] { return self.filter { (item) -> Bool in return item.id == id } } }
Alternatively, you can define a protocol extension method:
extension SequenceType where Generator.Element : Idable { func filterWithId(id : String) -> [Generator.Element] { return self.filter { (item) -> Bool in return item.id == id } } }
Then filterWithId()
is available to all types conforming to SequenceType
(in particular to Array
) if the sequence element type conforms to Idable
.
In Swift 3 this would be
extension Sequence where Iterator.Element : Idable { func filterWithId(id : String) -> [Iterator.Element] { return self.filter { (item) -> Bool in return item.id == id } } }