(Note: Full code is here, the Swift 4.2 toolchain wasn't liking my collection conditional conformance for some reason so you'll have to use a Swift 5 one)
I'm trying to make a struct that exposes a sequence/collection of adjacent pairs of elements in a base sequence/collection (so it would turn [1, 2, 3, 4]
into [(1, 2), (2, 3), (3, 4)]
). To do it, I'm following the (I think) standard practice of making the base struct that conforms to Sequence
and then conditional conformances to Collection
, BidirectionalCollection
, etc.
I'm modeling my Index as a pair of Base.Index
representing the two indices from the base collection. Its comparable conformance only compares the second index, since the other index should always be the one directly before it.
Based on that, for my conformance to Collection
, since I can't get the index before the base's endIndex
(since Base
is only a collection) I just do this for my endIndex
:
extension AdjacentPairsSequence: Collection where Base: Collection {
// Other collection conformance stuff
public var endIndex: Index {
return Index(first: base.endIndex, second: base.endIndex)
}
}
Since the endIndex
of a Collection
can only be used as a comparison point (and my comparison only checks the second element) this should be fine.
On the other hand, in the BidirectionalCollection
interface, that's no longer enough, since you can use the endIndex
to get indices before the endIndex
and those have to be valid. Because of that, I added a second endIndex
definition to the BidirectionalCollection
extension:
extension AdjacentPairsSequence: BidirectionalCollection where Base: BidirectionalCollection {
public var endIndex: Index {
let end = base.endIndex
guard end != base.startIndex else { return Index(first: end, second: end) }
return Index(first: base.index(before: end), second: end)
}
// index(before:) method
}
I was hoping that in use, BidirectionalCollection
s would get the second method and other Collection
s would get the first, which... sort of happens:
func printEndIndex<C: BidirectionalCollection>(_ c: C) {
print("End index as viewed from a generic function: \(c.endIndex)")
}
let col = [1, 2, 3].adjacentPairs
print("End index from a non-generic context: \(col.endIndex)") // Uses the BidirectionalCollection extension's method
printEndIndex(col) // Uses the Collection extension's method
Is this the intended behavior? Should I be able to define both of those methods at all?