Here is a bit more of the design I have in mind:
private extension RandomAccessCollection {
//This function is the core of the design I am proposing:
func _index(_ offset: Int) -> Self.Index {
// Rule: Positive offsets are always relative to `startIndex`,
// while negative offsets are always relative to `endIndex`.
let base = offset >= 0 ? startIndex : endIndex
return index(base, offsetBy: offset)
}
}
// We need our own range, because standard library ranges are not compatible with this design:
public struct OffsetRange {
public let start: Int
public let end: Int
}
// Only (partial) closed ranges make sense in this design:
public prefix func ... (rhs: Int) -> OffsetRange { return OffsetRange(start: 0, end: rhs) }
public postfix func ... (lhs: Int) -> OffsetRange { return OffsetRange(start: lhs, end: -1) }
public func ... (lhs: Int, rhs: Int) -> OffsetRange { return OffsetRange(start: lhs, end: rhs) }
public extension RandomAccessCollection {
subscript (o offset: Int) -> Element {
get { return self[_index(offset)] }
}
subscript(o range: OffsetRange) -> Self.SubSequence {
get { return self[_index(range.start) ... _index(range.end)] }
}
}
public extension RandomAccessCollection where Self: MutableCollection {
subscript (o offset: Int) -> Element {
get { return self[_index(offset)] }
set { self[_index(offset)] = newValue }
}
subscript(o range: OffsetRange) -> Self.SubSequence {
get { return self[_index(range.start) ... _index(range.end)] }
set { self[_index(range.start) ... _index(range.end)] = newValue }
}
}
let a = ["a","b","c","d","e","f","g"]
let b = a[2...]
print("b=\(b)")
// Forward offset:
for i in 0..<b.count { print(b[o: i], terminator: " ") }
print()
// Backward offset (from endIndex):
for i in -b.count..<0 { print(b[o: i], terminator: " ") }
print()
// Offset range:
print("b[o: 1...(-2)]=\(b[o: 1...(-2)])") // Note that -1 gives us the last element.
print("b[o: 1...3]=\(b[o: 1...3])")
print("b[o: -4...(-2)]=\(b[o: -4...(-2)])") // We can define `...-` to eliminate `()`.
print("b[o: -4...3]=\(b[o: -4...3])")
// Offset partial ranges:
print("b[o: 2...]=\(b[o: 2...])")
print("b[o: ...(-3)]=\(b[o: ...(-3)])")