Search code examples
swift

Understanding formIndex function to manipulate indices?


I want to know what does function

func formIndex(after i: inout Int)

According to documentation it says:

Replaces the given index with its successor.

But if we try to print out indices after using that function, it won't change:

var arr = [1,5,10, 15]

var index = arr.startIndex
arr.indices.forEach { index in
    print("before form index \(index)")
}

arr.formIndex(after: &index)
arr.indices.forEach { index in
    print("after form index \(index)")
}

Output:

before form index 0
before form index 1
before form index 2
before form index 3
after form index 0
after form index 1
after form index 2
after form index 3

It became even more confusing when I'm trying to print out value on that index:

var arr = [1,5,10, 15]

var index = arr.startIndex
arr.indices.forEach { index in
    print("before form index \(index)")
}

print("index is \(index)")
print("arr zero index value before form index \(arr[0])")
print("arr first index value before form index \(arr[1])")
print("arr index value before form index \(arr[index])")

arr.formIndex(after: &index)
arr.indices.forEach { index in
    print("after form index \(index)")
}

print("arr zero index value after form index \(arr[0])")
print("arr first index value after form index \(arr[1])")
print("arr index value after form index \(arr[index])")

Output:

before form index 0
before form index 1
before form index 2
before form index 3
index is 0
arr zero index value before form index 1
arr first index value before form index 5
arr index value before form index 1
after form index 0
after form index 1
after form index 2
after form index 3
arr zero index value after form index 1
arr first index value after form index 5
arr index value after form index 5

It looks like even index value is 0, we have different results for value at arr[0] and arr[index].


Solution

  • You have misunderstood what formIndex(after:) does. It does not modify the array in any way (it's not marked with mutating after all). All it does is it sets the inout parameter you passed, to the next index. Here is a demonstration:

    var arr = [1,5,10,15]
    var index = arr.startIndex
    print(index) // prints "0"
    arr.formIndex(after: &index)
    print(index) // prints "1"
    

    index is initially 0. After you pass it to formIndex(after:), it becomes 1, because 1 is the next index after 0. This is all it does.

    In the wording of the documentation, "replaces the given index" refers to setting the inout parameter to a different value, and "its successor" refers to an index, not an element in the array.


    Why would such a method be useful? Certainly, it is rarely useful to call this on an Array. To get the next index of an Array, you can just add 1. But if you are working with an arbitrary Collection (writing generic code), this is very useful. An arbitrary Collection doesn't necessarily use an incrementing sequence of Ints as its indices. For example, Dictionary is a Collection, and it uses Dictionary.Index values as its indices.

    So the purpose of formIndex(after:) is to give you a way to find the next index of a given index for any Collection.

    As an example, this is used in the implementation of firstIndex(of:):

    public func firstIndex(of element: Element) -> Index? {
      if let result = _customIndexOfEquatableElement(element) {
        return result
      }
    
      var i = self.startIndex
      while i != self.endIndex {
        if self[i] == element {
          return i
        }
        self.formIndex(after: &i)
      }
      return nil
    }
    

    In fact, you are required to implement a similar method - index(after:) - when conforming to the Collection protocol. The default implementation of formIndex(after:) will set the inout parameter to whatever index(after:) returns.

    You can think of formIndex(after:) as just an "in-place" version of index(after:). By using an inout parameter, the compiler can optimise it to a pass-by-reference (as if it is taking a pointer), which avoids copying the index value. Since the index type can be an arbitrary type, copying the index value might be expensive.