Search code examples
swiftstringsubscript

Swift 3 subscript range works for first cluster but not for middle


I'm trying to figure out why the following works on the first string cluster (character) but not on a second one. Perhaps the endIndex cannot be applied on another String?

let part = "A"
let full = "ABC"

print(full[part.startIndex ... part.startIndex])                    // "A"
print(full[part.endIndex ... part.endIndex])                        // ""   <- ???
print(full[part.endIndex ... full.index(after: part.endIndex)])     // "B"

bSecond should hold "B", but instead is empty. But the proof that one string index works on another is that the last statement works.

EDIT: Assuming full.hasPrefix(part) is true.

Swift puzzles.


Solution

  • You cannot use the indices of one string to subscript a different string. That may work by chance (in your first example) or not (in your second example), or crash at runtime.

    In this particular case, part.endIndex (which is the "one past the end position" for the part string) returns

    String.UnicodeScalarView.Index(_position: 1), _countUTF16: 0)
    

    with _countUTF16: (which is the "count of this extended grapheme cluster in UTF-16 code units") being zero, i.e. it describes a position (in the unicode scalar view) with no extent. Then

    full[part.endIndex ... part.endIndex]
    

    returns an empty string. But that is an implementation detail (compare StringCharacterView.swift). The real answer is just "you can't do that".

    A safe way to obtain the intended (?) result is

    let part = "A"
    let full = "ABC"
    
    if let range = full.range(of: part) {
        print(full[range]) // A
        if range.upperBound != full.endIndex {
            print(full[range.upperBound...range.upperBound]) // B
        }
    }