String.UnicodeScalarView.Index issue

See above playground.

The unicodeScalars.map and count are equal. But the index doesn't. Why aren't the indices equal? For unicodeScalars, the position should be the same, in my opinion.

source code

import Foundation

let ns:NSString = "A 🐵 makes 🙈🙉🙈🙉."
let s = ns as String
let s1 = s + ""
let monkeyFace:Character = "🙈"
let monkeyFaceScalar = UnicodeScalar(String(monkeyFace))!

if let i = s.unicodeScalars.firstIndex(of: monkeyFaceScalar),
    let i1 = s1.unicodeScalars.firstIndex(of: monkeyFaceScalar) {
    
    print(i == i1)
    print(s.unicodeScalars[i])
    print(s1.unicodeScalars[i1])
}

print(s.unicodeScalars.count == s1.unicodeScalars.count)
print(s.unicodeScalars.map({$0}) == s1.unicodeScalars.map{$0})
// prints true

Indices are not interchangeable between collections, except for a few exceptions like Array. If you want to compare positions between two different instances of a string, you’ll have to find the distance from startIndex and compare that. Comparing indices directly may not work since they can be implemented in term of pointers within the data structure.

Is this rule really documented? AFAK, I haven't read it in String.Index documentation.

It seems to be heavily implied by SE-0065 since you need the corresponding collection to mutate the index validly.

IIUC, for current implementation details, String.Index (which is shared among all its views) uses code points, which may differ among different encodings.

FWIW, I couldn't reproduce it on Swift 5.0.1 (Ubuntu 18.04). i == i1 returns true.

Found it, in Accessing Individual Element section here.

All other values of the Index type, such as the startIndex property of a different collection, are invalid indices for this collection.

1 Like

My code was tested with Swift 5.1 of Xcode 11 beta 3 in macOS 10.15 beta 3.

The link really helps and thank you.

1 Like