[swift-users] Collection Oddities

Guillaume Lessard glessard at tffenterprises.com
Tue Feb 7 22:14:36 CST 2017


I keep running into weird things with Swift 3 Collections.

A) Collection has its count property defined as an Int via IndexDistance:

public protocol Collection : Indexable, Sequence {
    associatedtype IndexDistance : SignedInteger = Int  // line 182 in Collection.swift
    public var count: IndexDistance { get }             // line 776
}

Given this, why can’t `count` be treated as an Int?

func intCount<C: Collection>(of c: C) -> Int {
  return c.count // nope!
}

A numericCast is required here in order to “convert” what is known-to-be-an-Int to an actual Int.
Why this is so? IndexDistance is defined such that it must be an integer (“A type that represents the number of steps between a pair of indices”)...
What is gained by making it so generic that it can’t simply be an Int?


B) Collection.Indices is a Collection of Index, *but*
- Collection.Indices.Index is not the same type as Collection.IndexDistance
- Collection.Indices.Iterator.Element is somehow not the automatically the same type as Collection.Index
(ugh)

The second seems like a conditional conformance issue, but the first one is baffling.

I did find that with String.CharacterView:
String.CharacterView.Indices.Index != String.CharacterView.IndexDistance
although, as expected,
String.CharacterView.Indices.IndexDistance == String.CharacterView.IndexDistance
(which provides an clear workaround.)


***
I wanted to extend Collection with concurrentPerform; which seems simple on the surface:

extension Collection {
  public func concurrentPerform(task: @escaping (Self.Iterator.Element) -> Void) {
    DispatchQueue.concurrentPerform(iterations: count) {
      iteration in
      task(self[indices[iteration]])
    }
  }
}
… but that won’t do.

The closest thing I found that does work is this:

extension Collection {
  public func concurrentPerform(task: @escaping (Self.Iterator.Element) -> Void) {
    let count: Int = numericCast(self.count)
    let indexList = (0..<count).map { index(startIndex, offsetBy: numericCast($0)) }

    DispatchQueue.concurrentPerform(iterations: count) {
      iteration in
      task(self[indexList[iteration]])
    }
  }
}

Note the unfortunate creation of an array of Index.
Generic Collection code is exhausting!

Cheers,
Guillaume Lessard



More information about the swift-users mailing list