# \[Rant\] Indexing into ArraySlice

**URL:** <https://forums.swift.org/t/rant-indexing-into-arrayslice/14105>\
**Category:** Using Swift\
**Created:** [June 29, 2018, 10:17pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105 "2018-06-29T22:17:35Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![paiv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paiv/32/4041_2.png) [@paiv](https://forums.swift.org/u/paiv)\
**Post date:** [June 29, 2018, 10:17pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/1 "2018-06-29T22:17:35Z")

</div>

Here is an anecdote, can you guess the output?

```swift
let abc = Array("abcdef")
let some = abc[1...]
some[1] == abc[1]
some[1] == abc[2]
let same = some.map{ $0 }
same[1] == abc[1]
same[1] == abc[2]

```

Output:

```swift
["a", "b", "c", "d", "e", "f"]
["b", "c", "d", "e", "f"]
true
false
["b", "c", "d", "e", "f"]
false
true

```

I believe, shared indexing is performance optimization at the expense of usability. A slice looks in every way just like an array, except for indexing. I have to be very careful with indexing when there are slices involved. But since this happens not very often, it is easy to forget, and face runtime error. With inferred types, it is easy to misread.

Another exercise: take a slice from a slice and try indexing into that.

This is so confusing. Do you know any other language with this design?

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [June 29, 2018, 10:27pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/2 "2018-06-29T22:27:16Z")

</div>

It wasn't intended as a performance optimization, but to improve the usability of slices in divide-and-conquer algorithms, since you can take the indexes from a slice and use them as-is on the original whole collection or an overlapping slice without having to track adjustments. @dabrahams may have more background on the design choice here.

---

<div class="post-metadata">

**Author:** ![paiv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paiv/32/4041_2.png) [@paiv](https://forums.swift.org/u/paiv)\
**Post date:** [June 29, 2018, 10:36pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/3 "2018-06-29T22:36:19Z")

</div>

In my experience, this leads to me forgetting to wrap

```swift
abc[1...]

```

with

```swift
Array(abc[1...])

```

getting no compile error, but crashing run time.

Definitely would vote against it.

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [June 29, 2018, 10:49pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/4 "2018-06-29T22:49:13Z")

</div>

The `Array()` call is going to cause a copy that's otherwise unnecessary, which is a lot of overhead just to get zero-based indexing. Instead of using absolute indexes with slices, you might have an easier time making your indexes `startIndex`-relative for the slice. This'll be easier for the future if you choose to genericize the algorithm to work with non-Array collections too.

---

<div class="post-metadata">

**Author:** ![AlexanderM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/alexanderm/32/9781_2.png) [@AlexanderM](https://forums.swift.org/u/AlexanderM)\
**Post date:** [June 29, 2018, 10:51pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/5 "2018-06-29T22:51:10Z")

</div>

I think this is the most sensible choice for a default behaviour, but I think it would be best to also provide a `subscript(zeroIndexed index: Int) -> T`

Try implementing this with 0 based indexing, and see how much uglier it gets:

```
func binarySearch<T: Equatable & Comparable>(_ array: [T], for desired: T) -> Int? {
	func binarySearch(_ slice: ArraySlice<T>, for desired: T) -> Int? {
		let midIndex = slice.index(slice.startIndex, offsetBy: slice.count / 2)
		let mid = slice[midIndex]
		if mid < desired {
			return binarySearch(slice[midIndex...], for: desired)
		} else if mid > desired {
			return binarySearch(slice[...midIndex], for: desired)
		}
		else {
			return midIndex
		}
	}
	
	return binarySearch(ArraySlice(array), for: desired)
}

print(binarySearch(Array(100...5000000), for: 150))

```

---

<div class="post-metadata">

**Author:** ![scanon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/scanon/32/2445_2.png) [@scanon](https://forums.swift.org/u/scanon)\
**Post date:** [June 29, 2018, 11:16pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/6 "2018-06-29T23:16:54Z")

</div>

> Try implementing this with 0 based indexing, and see how much uglier it gets:

Debatably, it's less messy:

```swift
func binarySearch<T: Equatable & Comparable>(_ array: [T], for desired: T) -> Int? {
	func binarySearch(_ slice: ArraySlice<T>, for desired: T) -> Int? {
		let midIndex = slice.count / 2
		let mid = slice[midIndex]
		if mid < desired {
			return midIndex + binarySearch(slice[midIndex...], for: desired)
		}
		if mid > desired {
			return binarySearch(slice[...midIndex], for: desired)
		}
		return midIndex
	}
	return binarySearch(ArraySlice(array), for: desired)
}

```

What changes? We pick up an extra `midIndex +` on one branch, but we make the computation of `midIndex` itself a lot more intuitive.

---

<div class="post-metadata">

**Author:** ![paiv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paiv/32/4041_2.png) [@paiv](https://forums.swift.org/u/paiv)\
**Post date:** [June 29, 2018, 11:19pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/7 "2018-06-29T23:19:59Z")

</div>

That would be

```swift
func binarySearch<T>(_ array: [T], for desired: T) -> Int? where T: Comparable {
    func binarySearch(_ slice: ArraySlice<T>, for desired: T) -> Int? {
        let midIndex = slice.count / 2
        let mid = slice[midIndex]
        if mid < desired {
            return binarySearch(slice[midIndex...], for: desired) .map{ $0 + midIndex }
        } else if mid > desired {
            return binarySearch(slice[...midIndex], for: desired)
        }
        else {
            return midIndex
        }
    }
    
    return binarySearch(ArraySlice(array), for: desired)
}

```

How much is it uglier?

---

<div class="post-metadata">

**Author:** ![paiv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paiv/32/4041_2.png) [@paiv](https://forums.swift.org/u/paiv)\
**Post date:** [June 29, 2018, 11:22pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/8 "2018-06-29T23:22:11Z")

</div>

It has optional return, so you can't just `+`

---

<div class="post-metadata">

**Author:** ![sbromberger](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sbromberger/32/3261_2.png) [@sbromberger](https://forums.swift.org/u/sbromberger)\
**Post date:** [June 29, 2018, 11:22pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/9 "2018-06-29T23:22:29Z")

</div>

As a new user, this slice behavior puzzled me. It seems that the only benefit to a slice (besides zero allocations) is that it's a (run-time) error to index it below a certain point. This doesn't seem like a huge benefit.

---

<div class="post-metadata">

**Author:** ![scanon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/scanon/32/2445_2.png) [@scanon](https://forums.swift.org/u/scanon)\
**Post date:** [June 29, 2018, 11:26pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/10 "2018-06-29T23:26:32Z")

</div>

This behavior isn't really a huge deal to me either way, except that more generally as a mathematician it vaguely bothers me that we have a canonical map from the non-negative integers to the set of indices given by 0 --\> `startIndex` and + --\> `indexAfter` but for some reason we've chosen not to identify them.

It's a quirk, and somewhat weird coming from other languages I've used, but it hasn't proven to be a huge deal in my personal experience.

---

<div class="post-metadata">

**Author:** ![scanon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/scanon/32/2445_2.png) [@scanon](https://forums.swift.org/u/scanon)\
**Post date:** [June 29, 2018, 11:27pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/11 "2018-06-29T23:27:18Z")

</div>

Yeah, yeah, `.map { /blah/ }`. Sorry, coding in browser =)

---

<div class="post-metadata">

**Author:** ![paiv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paiv/32/4041_2.png) [@paiv](https://forums.swift.org/u/paiv)\
**Post date:** [June 29, 2018, 11:35pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/12 "2018-06-29T23:35:39Z")

</div>

> [@Joe\_Groff](#):
>
> The `Array()` call is going to cause a copy that's otherwise unnecessary, which is a lot of overhead just to get zero-based indexing.

With zero-based indexing I would not have to call `Array()`

> [@Joe\_Groff](#):
>
> Instead of using absolute indexes with slices, you might have an easier time making your indexes `startIndex` -relative for the slice.

Instead of tracking which variable is `Array`, which is `ArraySlice`, and selectively applying `.startIndex`, I'd rather have zero-based indexing on slices.

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [June 29, 2018, 11:42pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/13 "2018-06-29T23:42:54Z")

</div>

On the other hand, if you always use `.startIndex`, you also don't need to track which variables are Arrays. I would say that Collection indexes are more like pointers or C++-style iterators (`Cursor` would've been a more honest name IMO); they just happen to look like traditional integer indexes on Array.

---

<div class="post-metadata">

**Author:** ![dabrahams](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dabrahams/32/9815_2.png) [@dabrahams](https://forums.swift.org/u/dabrahams)\
**Post date:** [June 29, 2018, 11:56pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/14 "2018-06-29T23:56:32Z")

</div>

> [@Joe\_Groff](#):
>
> It wasn't intended as a performance optimization, but to improve the usability of slices in divide-and-conquer algorithms,

Not to improve usability in divide-and-conquer algorithms, but to make them usable _at all_ in generic divide-and-conquer algorithms. Not all collections have the ability to measure distances between, and offset, indices in O(1).

---

<div class="post-metadata">

**Author:** ![paiv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paiv/32/4041_2.png) [@paiv](https://forums.swift.org/u/paiv)\
**Post date:** [June 29, 2018, 11:57pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/15 "2018-06-29T23:57:18Z")

</div>

> [@Joe\_Groff](#):
>
> they just happen to look like traditional integer indexes on Array.

From user perspective, this is a promise not kept. Because a collection accepting integer index mentally is thought to behave as array indexing. It adds cognitive load on user to take them apart, where language should be unambiguous. Either don't allow integer indexing on slices, or be array-like on slices.

---

<div class="post-metadata">

**Author:** ![dabrahams](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dabrahams/32/9815_2.png) [@dabrahams](https://forums.swift.org/u/dabrahams)\
**Post date:** [June 30, 2018, 12:01am UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/16 "2018-06-30T00:01:28Z")

</div>

Yes, there are a set of design tradeoffs here. Having nonzero-based indexing with integers is not ideal, but it was the best compromise overall. [Stop writing your algorithms specifically for arrays](https://developer.apple.com/videos/play/wwdc2018/223/), and you won't have this problem.

---

<div class="post-metadata">

**Author:** ![paiv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/paiv/32/4041_2.png) [@paiv](https://forums.swift.org/u/paiv)\
**Post date:** [June 30, 2018, 12:13am UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/17 "2018-06-30T00:13:37Z")

</div>

When I have array on input, I work with array. I don't have time to generalize. Basically you are telling me that _I'm holding it wrong_.

Anyway this is a rant. I'm pointing that language breaks user expectations, and user have to learn their way through runtime errors.

---

<div class="post-metadata">

**Author:** ![palimondo](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/palimondo/32/1144_2.png) [@palimondo](https://forums.swift.org/u/palimondo)\
**Post date:** [June 30, 2018, 7:21pm UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/18 "2018-06-30T19:21:21Z")

</div>

> [@dabrahams](#):
>
> [Stop writing your algorithms specifically for arrays](https://developer.apple.com/videos/play/wwdc2018/223/), and you won't have this problem.

Fully agreed. But it would be much easier to argue this case if standard library shipped with at least [one tree collection](https://github.com/attaswift/BTree)…

---

<div class="post-metadata">

**Author:** ![jawbroken](https://avatars.discourse-cdn.com/v4/letter/j/cc9497/32.png) [@jawbroken](https://forums.swift.org/u/jawbroken)\
**Post date:** [July 1, 2018, 7:57am UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/19 "2018-07-01T07:57:01Z")

</div>

Why are those things related at all? There are several collections that don't have integer indices, starting from 0, in the standard library already.

---

<div class="post-metadata">

**Author:** ![palimondo](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/palimondo/32/1144_2.png) [@palimondo](https://forums.swift.org/u/palimondo)\
**Post date:** [July 2, 2018, 4:09am UTC](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105/20 "2018-07-02T04:09:57Z")

</div>

The key step in learning about Swift collections is to think of indexes as **abstract pointers** into the collection. Pointing at a tree nodes comes naturally during their typical usage. This isn’t the case with `Set` and `Dictionary`. `Array` doesn’t help you make this mental leap at all, because it’s contiguously ordered in memory and its `Index` is `Int`, which leads people into demanding zero based indexing for `Slice`s.

@paiv General point is, when you want to subscript into a collection, you should get your index from that collection. `Int` indexes are easy to form in your head and when you hardcode them into your subscripts, you can be surprised by the result.

When you realize that `Slice`s are just views into the underlying collection, it makes perfect sense why they would share indices.

Remember that when you have an index from array, it’s a logical pointer to the element, not an ordinal position from the beginning of an array. For that you should use index arithmetic:  
`a.index(a.startIndex, offsetBy: 3)`

[Next page](https://forums.swift.org/t/rant-indexing-into-arrayslice/14105.md?page=2)
