For this specific use-case I'd use a custom post-increment operator:
array[index++]
I use that in my sources occasionally, so far had no problem with it.
However there are indeed cases where "inout" subscript parameter would be highly beneficial to have. Alternatively a function with setter
could work equally well:
func foo() -> String {
get {
// normal function body here
}
set {
print(newValue)
// set is mutating unless mark nonmutating
}
}
foo() = "hello"
Without any of those, there's this dirty workaround:
extension MutableCollection {
subscript(postIncrement i: UnsafeMutablePointer<Index>) -> Element {
get {
defer { formIndex(after: &i.pointee) }
return self[i.pointee]
}
set {
defer { formIndex(after: &i.pointee) }
self[i.pointee] = newValue
}
}
}
var array = ["a", "b", "c", "d"]
var index = 0
let x = withUnsafeMutablePointer(to: &index) { array[postIncrement: $0] }
print(x, index)
let y = withUnsafeMutablePointer(to: &index) { array[postIncrement: $0] }
print(y, index)
let z = withUnsafeMutablePointer(to: &index) { array[postIncrement: $0] }
print(z, index)
// setter:
_ = withUnsafeMutablePointer(to: &index) { array[postIncrement: $0] = "X" }
print(array, index) // ["a", "b", "c", "X"] 4
which is far from nice.
Somewhat cleaner is to use a binding subscript parameter:
extension MutableCollection {
subscript(postIncrement i: Binding<Index>) -> Element {
get {
defer { formIndex(after: &i.wrappedValue) }
return self[i.wrappedValue]
}
set {
defer { formIndex(after: &i.wrappedValue) }
self[i.wrappedValue] = newValue
}
}
}
var array = ["a", "b", "c", "d"]
var index = 0
var binding = Binding<Int>(get: {index}, set: {index = $0})
let x = array[postIncrement: binding]
print(x, binding.wrappedValue) // a 1
let y = array[postIncrement: binding]
print(y, binding.wrappedValue) // b 2
let z = array[postIncrement: binding]
print(z, binding.wrappedValue) // c 3
// setter:
array[postIncrement: binding] = "X"
print(array, binding.wrappedValue) // ["a", "b", "c", "X"] 4